1 
2 #ifndef _global_h
3 #	include "global.h"
4 #endif
5 
6 #include <X11/Xmd.h>
7 #include <X11/extensions/shape.h>
8 
9 #ifndef _objects_h
10 #	include "objects.H"
11 #endif
12 #ifndef _gifx_image_h
13 #	include "gifx_image.H"
14 #endif
15 #ifndef _imgbuff_h
16 #	include "imgbuff.H"
17 #endif
18 #ifndef _vec2_h
19 #	include "vec2.h"
20 #endif
21 #ifndef _vec2list_h
22 #	include "vec2list.h"
23 #endif
24 #ifndef _mat2_h
25 #	include "mat2.h"
26 #endif
27 #ifndef _color_mapper_h
28 #	include "color_mapper.H"
29 #endif
30 #ifndef _puzzle_h
31 #	include "puzzle.H"
32 #endif
33 
34 
35 /******************************************************************************
36 
37 PieceFrame    - corner, edge and pin-data of the piece including polygon-spline
38       Vec2List *vl
39 RotatedFrame  - position-data including
40                 window-position, upward-angle and rotated polygin spline
41       Vec2     winpos
42       Real     windir
43       Vec2List *tvl
44 BitmapPiece   - pixmap with mask of the rotated Piece
45 		Pixmap   tilemask
46 PixmapPiece   - pixmap with Puzzle-Image of the rotated Piece
47 		Pixmap   tilemap
48 ShadowPiece   - pixmap with Puzzle-Image and ShadowFrame of the rotated Piece
49 		Pixmap   shadowmask
50 		Pixmap   shadowmap
51 PieceObject   - object subclass to be stack-controlled
52 DBPieceObject - double buffered moves and turns
53 
54  ******************************************************************************/
55 
56 //  . . . . + . . . . + . . . . + . . . .
57 //  . . . . . . . . . . . . . . . . . . .
58 //  . * . . . . . . . . . . . . . . . . .
59 //  . . . . . . . . . . . . . . . . . . .
60 //  . . . . . . . . . . . . . . . . . . .
61 //  . . . . + . . . . + . . . . + . . . .
62 //  . . . . . . . . . . . . . . . . . . .
63 //  . * . . . . . . . . . . . . . . . . .
64 //  . . . . . . . . . * . . * . . . . . .
65 //  . . * . . . . . . . . . . . . . . . .
66 //  . . . . * . * . . + . . . . + * . . .
67 //  . . . . . . . . . . . . . . . . . . .
68 //  . . . . . . . . . . . . . . . . . . .
69 //  . . . . . . . . . . . . . . . . . . .
70 //  . . . . . . . . . . . . . . . . . * .
71 //  . . . . + . . . . + . . . . + . . . .
72 //  . . . . . . . . . . . . . . . . . * .
73 
74 double PieceFrameSetup::spx[] = {  1, 2.5, 5, 7, 7, 5, 5, 6, 8,13 };
75 double PieceFrameSetup::spy[] = { 13,12.5,11, 8, 5, 2, 0,-2,-3,-3 };
76 
77 
PieceFrameSetup()78 PieceFrameSetup::PieceFrameSetup() {
79 }
80 
~PieceFrameSetup()81 PieceFrameSetup::~PieceFrameSetup() {
82 }
83 
Init(const Vec2 & tl,const Vec2 & tr,const Vec2 & br,const Vec2 & bl)84 void PieceFrameSetup::Init( const Vec2 &tl, const Vec2 &tr, const Vec2 &br, const Vec2 &bl ) {
85 	center = (tl+tr+br+bl)/4;
86 	v[0] = tl-center;
87 	v[1] = tr-center;
88 	v[2] = br-center;
89 	v[3] = bl-center;
90 	pin[0] = pin[1] = pin[2] = pin[3] = 0;
91 
92 #if (0)
93 	printf( "%g %g             %g %g\n", (double)tl.X(), (double)tl.Y(), (double)tr.X(), (double)tr.Y() );
94 	printf( "center: %g %g\n", (double)center.X(), (double)center.Y() );
95 	printf( "%g %g             %g %g\n", (double)bl.X(), (double)bl.Y(), (double)br.X(), (double)br.Y() );
96 #endif
97 }
98 
PositionChanged()99 void PieceFrame::PositionChanged()	{}
DirectionChanged()100 void PieceFrame::DirectionChanged()	{}
101 
PieceFrame()102 PieceFrame::PieceFrame() {
103 	vl = 0;
104 	join_count=1;
105 }
106 
~PieceFrame()107 PieceFrame::~PieceFrame() {
108 	if (vl)		delete vl;
109 }
110 
Init(const PieceFrameSetup & pfs)111 const Vec2List &PieceFrame::Init( const PieceFrameSetup &pfs ) {
112 
113 	center=pfs.center;
114 	vl = new Vec2List(MaxSplineLen);
115 
116 	for (int i=0;i<4;i++) {
117 		Vec2	s(pfs.v[i]);
118 
119 		if (pfs.pin[i]) {
120 			int	j;
121 			Vec2	e(pfs.v[(i+1)%4]);		// end of segment
122 			Vec2	d=e-s;				// edge vector
123 
124 			Vec2	m1=s+pfs.pin[i]*d;		// medium point for pin
125 			Vec2	bw=(s-m1)/24.0;	// backstep vector to startpoint
126 			Vec2	fw=(e-m1)/24.0;	// forward vector to endpoint
127 
128 			Vec2	n=d/44;
129 			Vec2	o=(pfs.left[i]?n.TurnLeft():n.TurnRight());
130 
131 			*vl |= s;
132 			for (j=1;j<=HalfSplineLen;j++) {
133 				*vl |= m1 + pfs.spx[HalfSplineLen-j]*bw + pfs.spy[HalfSplineLen-j]*o;
134 			}
135 			for (j=0;j<HalfSplineLen;j++) {
136 				*vl |= m1 + pfs.spx[j]*fw + pfs.spy[j]*o;
137 			}
138 		}
139 		else {
140 			*vl |= s;
141 		}
142 	}
143 	// *vl |= pfs.v[0];
144 	return *vl;
145 }
146 
147 // ===========================================================================
148 
TwinPieceFrame()149 TwinPieceFrame::TwinPieceFrame() {
150 	page=0;
151 }
152 
~TwinPieceFrame()153 TwinPieceFrame::~TwinPieceFrame() {
154 }
155 
FlipPage()156 void TwinPieceFrame::FlipPage() {
157 	page=1-page;
158 	DirectionChanged();
159 }
160 
161 // ===========================================================================
162 
RotatedFrame()163 RotatedFrame::RotatedFrame() {
164 	tvl = 0;
165 }
~RotatedFrame()166 RotatedFrame::~RotatedFrame() {
167 	if (tvl)		delete tvl;
168 }
169 
Init(const PieceFrameSetup & pfs)170 void RotatedFrame::Init( const PieceFrameSetup &pfs ) {
171 	PieceFrame::Init( pfs );
172 	windir = 0;
173 	winpos = Vec2Zero;
174 }
175 
CheckForJoin(class RotatedFrame * obj)176 int RotatedFrame::CheckForJoin( class RotatedFrame *obj ) {
177 Real	windir_delta = windir - obj->windir;
178 
179 	// check for correct page
180 	if (page!=obj->page)										return 0;
181 
182 	// check for an angle between -10 and +10 degrees
183 	if (fmod(windir_delta+380.0,360.0)>40)				return 0;
184 
185 
186 Vec2	v1(center - obj->center);
187 Vec2	v2(winpos - obj->winpos);
188 Real	help=fabs(windir-obj->windir);
189 Real	mid_angle;
190 		if (help>180.0)	mid_angle=fmod((windir+obj->windir+360.0)/2,360.0);
191 		else					mid_angle=fmod((windir+obj->windir)/2,360.0);
192 Vec2	erg;
193 		v2=v2.TurnAngleDeg( -mid_angle );
194 
195 		if (page) { erg=Vec2(v1.X()+v2.X(),v1.Y()-v2.Y());
196 		}
197 		else			erg=Vec2(v1.X()-v2.X(),v1.Y()-v2.Y());
198 
199 
200 #ifdef DEBUG
201 	v2=v2.TurnAngleDeg( mid_angle );
202 
203 	printf( "ImageA: %6.2f %6.2f  WindowA: %6.2f %6.2f  at angle: %6.2f\n",
204 		(double)center.X(), (double)center.Y(), (double)winpos.X(), (double)winpos.Y(), (double)windir );
205 	printf( "ImageB: %6.2f %6.2f  WindowB: %6.2f %6.2f  at angle: %6.2f\n",
206 		(double)obj->center.X(), (double)obj->center.Y(), (double)obj->winpos.X(), (double)obj->winpos.Y(), (double)obj->windir );
207 	printf( "        %6.2f %6.2f          %6.2f %6.2f\n",
208 		(double)v1.X(), (double)v1.Y(), (double)v2.X(), (double)v2.Y() );
209 
210 	v2=v2.TurnAngleDeg( -mid_angle );
211 
212 	printf( "        angle: %6.2f          %6.2f %6.2f\n",
213 			mid_angle, (double)v2.X(), (double)v2.Y() );
214 	printf( "                               %6.2f %6.2f\n",
215 			(double)erg.X(), (double)erg.Y() );
216 #endif
217 
218 
219 	if (erg.Norm()<tile_size/3) {
220 			DBG0( "***** HIT *****\n" );
221 			return 1;
222 	}
223 	return 0;
224 }
225 
PositionChanged()226 void RotatedFrame::PositionChanged() {
227 }
DirectionChanged()228 void RotatedFrame::DirectionChanged() {
229 	if (tvl)		delete tvl;
230 	if (!page) {
231 		tvl = new Vec2List(GetPolyLine());
232 	}
233 	else {
234 		tvl = new Vec2List(vl->Len());
235 		for (int i=0;i<vl->Len();i++) {
236 			tvl->AddAt(i,Vec2( -(*vl)[i].X(), (*vl)[i].Y() ));
237 		}
238 	}
239 	tvl->TurnAngleDeg(windir);
240 }
241 
242 // DoJoin:
243 // A Joined segment in both polylines has to be found. When found
244 // the points out of that segment are added from the second to the first
245 // polyline, which will result into the merge of the two outlines.
246 //
247 // vl:       +----+----+----+----+----+----+----+----
248 //           1    2    3    4    s j  e    7    8
249 // obj->vl:       +----+----+----+----
250 //                1'   e  i s    4'
251 // result:   +----+----+----+----+----+----+----+----+----+----
252 //           1    2    3    4    s    4'   1'   e    7    8
253 //
254 // special cases:
255 // obj->vl:       +----+----+----+----
256 //                1'   2' i s    4'
257 //                          e
258 
FindStartForJoin(class PieceFrame * obj)259 int RotatedFrame::FindStartForJoin( class PieceFrame *obj ) {
260 int i,j;
261 
262 	for (i=0;i<obj->vl->Len();i++) {
263 		for (j=0;j<vl->Len();j++) {
264 			if (same_point(j,obj,i))		return i;
265 		}
266 	}
267 	return -1;
268 }
269 
DoJoin(class RotatedFrame * obj,int i,int swap)270 int RotatedFrame::DoJoin( class RotatedFrame *obj, int i, int swap ) {
271 int j,k;
272 
273 	for (j=0;j<vl->Len();j++) {
274 		if (same_point(j,obj,i)) {
275 		// found intersecting point: now find start and end of intersection
276 			int s,e;
277 			int sj;
278 			int ej;
279 			int c=1;
280 
281 			for (s=1;s<obj->vl->Len();s++) {
282 				int si=(i+s)%obj->vl->Len();
283 				sj=((j-s)+vl->Len())%vl->Len();
284 				if (!same_point(sj,obj,si))	break;
285 				c++;
286 			}
287 			s--;
288 			sj=((j-s)+vl->Len())%vl->Len();
289 			for (e=1;e<obj->vl->Len();e++) {
290 				int ei=((i-e)+obj->vl->Len())%obj->vl->Len();
291 				ej=(j+e)%vl->Len();
292 				if (!same_point(ej,obj,ei))	break;
293 				c++;
294 			}
295 			e--;
296 			ej=(j+e)%vl->Len();
297 
298 		// found start and end of intersection as in shown in diagramm
299 		// now the points have to be moved to the destination polygon
300 			DBG2( "*** Range: %d %d\n", sj, ej );
301 			if ((sj==ej)&&(c>1)) {
302 				//
303 				// when a surrounding tile is moved onto of the inner tile, the
304 				// problem arise that the whole inner tile and the partial surrounding
305 				// tile has to be deleted. Since this usually happens the other way round:
306 				// "The hole is filled with the inner tile.", this special case is reversed
307 				// by swapping both tiles and redoing the join, in which the partial list
308 				// of the surrounding tile will be found in sj - ej
309 				//
310 				DBG0( "*** special inner case: do reverse join\n" );
311 				Vec2	help_center=center;
312 				center=obj->center;
313 				obj->center=help_center;
314 
315 				Vec2List *help_list=vl;
316 				vl=obj->vl;
317 				obj->vl=help_list;
318 
319 				return DoJoin(obj,j,1);
320 			}
321 			join_count+=obj->join_count;
322 
323 			vl->DelRange(sj,ej,&sj);
324 
325 			k=(-e+obj->vl->Len())%obj->vl->Len();
326 			if ( k!=s || (sj==ej&&c==1) ) {
327 				DBG1( "*** Insert at: %d -", sj );
328 				do {
329 					DBG1( " %d", (i+k)%obj->vl->Len() );
330 					vl->AddAt(sj+1,(*obj->vl)(i+k)+obj->center-center);
331 					k=(k-1+obj->vl->Len())%obj->vl->Len();
332 				}
333 				while( k!=s );
334 				DBG0( "\n" );
335 			}
336 			else {
337 				// an inner tile was removed from the polyline and therefore
338 				// some kind of one way trail to the inner tile remained in the
339 				// polyline which should be removed with a tiny "garbage" collector.
340 				DBG1( "*** Garbage Deleted (from: %d)", vl->Len() );
341 				for (j=0;j<vl->Len()-2;j++) {
342 					while (same_point(j,this,j+2)) {
343 						DBG1( "%d ", j );
344 						vl->DelRange(j,j+2,&j);
345 						if (j>0)	j--;
346 					}
347 				}
348 				DBG1( " (to: %d)\n", vl->Len() );
349 			}
350 
351 		// the new extend has to be queried to setup the center
352 			Vec2	tl, br;
353 			vl->GetExtent( &tl, &br );
354 			Vec2	new_center( center+((tl+br)/2) );
355 
356 #if (0)
357 	printf( "    center: %g %g\n", center.X(), center.Y() );
358 	printf( "      from: %g %g\n", tl.X(), tl.Y() );
359 	printf( "        to: %g %g\n", br.X(), br.Y() );
360 	printf( "new center: %g %g\n", new_center.X(), new_center.Y() );
361 #endif
362 
363 			if (swap) {
364 				// this time the second objects position should be remained
365 					winpos = obj->winpos-(obj->center-new_center).ScaleX(page?-1:1).TurnAngleDeg(obj->windir);
366 			}
367 			else	winpos -= (center-new_center).ScaleX(page?-1:1).TurnAngleDeg(windir);
368 
369 			*vl += (center-new_center);
370 			center=new_center;
371 
372 			PositionChanged();
373 			DirectionChanged();
374 			return 1;
375 		}
376 	}
377 	return 0;
378 }
379 
380 // ===========================================================================
381 
FlipFrame()382 FlipFrame::FlipFrame() {
383 	ftvl=0;
384 	itm=0;
385 }
386 
~FlipFrame()387 FlipFrame::~FlipFrame() {
388 	if (ftvl)	delete ftvl;
389 	if (itm)		delete itm;
390 }
391 
StartFlip(const Real & angle)392 void FlipFrame::StartFlip( const Real &angle ) {
393 	mangle = angle;
394 }
StopFlip()395 void FlipFrame::StopFlip() {
396 	if (ftvl)	delete ftvl;
397 	ftvl=0;
398 	if (itm)			delete itm;
399 	itm=0;
400 }
401 
SetFlip(const Real & current)402 void FlipFrame::SetFlip( const Real &current ) {
403 	if (ftvl)		delete ftvl;
404 	if (itm)			delete itm;
405 
406 	itm = new Mat2();					// set up transformation
407 	if (page)	*itm = (*itm).ScaleX( -1 );
408 	*itm = (*itm).RotateDeg( -windir )
409 					.Rotate( mangle )
410 					.ScaleX( current )
411 					.Rotate( -mangle );
412 
413 
414 	ftvl = new Vec2List(RotatedFrame::GetPolyLine(),*itm);
415 	*itm = !*itm;						// reverse transformation for texture-mapping
416 	DirectionChanged();
417 }
418 
GetTPolyLine()419 Vec2List &FlipFrame::GetTPolyLine() {
420 	if (!ftvl)			return *tvl;
421 	else					return *ftvl;
422 }
423 
424 // ===========================================================================
425 
426 GC BitmapPiece::gcb = 0;
427 
BitmapPiece()428 BitmapPiece::BitmapPiece() {
429 	tilemask = 0;
430 }
~BitmapPiece()431 BitmapPiece::~BitmapPiece() {
432 	DropBitmap();
433 }
DropBitmap()434 void BitmapPiece::DropBitmap() {
435 	if (tilemask) {
436 		XFreePixmap(dpy,tilemask);
437 		tilemask=0;
438 	}
439 }
440 
PositionChanged()441 void BitmapPiece::PositionChanged() {
442 	winx   = XPix(winpos.X())-offx;
443 	winy   = YPix(winpos.Y())-offy;
444 }
445 
DirectionChanged()446 void BitmapPiece::DirectionChanged() {
447 
448 	DropBitmap();
449 	FlipFrame::DirectionChanged();
450 
451 Vec2	tl,br;
452 int	x1,y1,x2,y2;
453 const Vec2List &poly( GetTPolyLine() );
454 
455 	poly.GetExtent( &tl, &br );
456 	x1 = XPix(tl.X());
457 	y1 = YPix(tl.Y());
458 	x2 = XPix(br.X());
459 	y2 = YPix(br.Y());
460 
461 	offx   = -x1;
462 	offy   = -y1;
463 	winx   = XPix(winpos.X())-offx;
464 	winy   = YPix(winpos.Y())-offy;
465 	width  = x2-x1+1;
466 	height = y2-y1+1;
467 #if (0)
468 	printf( "%g %g  (%d %d)\n", (double)tl.X(), (double)tl.Y(), offx, offy );
469 	printf( "center: %g %g\n", (double)center.X(), (double)center.Y() );
470 	printf( "    %g %g\n", (double)br.X(), (double)br.Y() );
471 #endif
472 
473 	tilemask=XCreatePixmap(dpy,RootWindow(dpy,scr),width,height,1);
474 	if (!gcb)	gcb=XCreateGC(dpy,tilemask,0,0);
475 
476 	XSetForeground(dpy,gcb,0);								// clear the new bitmap
477 	XFillRectangle(dpy,tilemask,gcb,0,0,width,height);
478 
479 	XSetForeground(dpy,gcb,1);								// draw the polyline
480 XPoint	xpts[MaxSplineLen];
481 XPoint	*xpts_p;
482 	if ((unsigned)poly.Len()>(sizeof(xpts)/sizeof(XPoint))) {
483 		xpts_p=new XPoint[poly.Len()];					// create buffer when necessary
484 	}
485 	else xpts_p=xpts;
486 
487 	for (int i=0;i<poly.Len();i++) {							// create the pointlist
488 		Vec2	help( poly[i]-tl );
489 		xpts_p[i].x = XPix(help.X());
490 		xpts_p[i].y = YPix(help.Y());
491 	}
492 #if (0)
493 	XDrawLines( dpy, tilemask, gcb, xpts_p, poly.Len(), 0 );
494 #else
495 	XFillPolygon(dpy,tilemask, gcb, xpts_p, poly.Len(), 0, 0 );
496 #endif
497 
498 #if (0)
499 	XSetFunction(dpy,gcb,GXxor);							// draw a test-frame
500 	XDrawRectangle(dpy,tilemask,gcb,0,0,width-1,height-1);
501 	XSetFunction(dpy,gcb,GXcopy);
502 #endif
503 
504 	if (xpts_p!=xpts)		delete [] xpts_p;
505 }
506 
Redraw()507 void BitmapPiece::Redraw() {
508 	XSetClipMask(dpy,gc,GetBitmap());
509 	XSetClipOrigin(dpy,gc,winx,winy);
510 
511 // printf( "winpos: %g %g\n", (double)winpos.X(), (double)winpos.Y() );
512 // printf( "offx: %d %d\n", offx, offy );
513 	XFillRectangle(dpy,win,gc, winx, winy, width, height );
514 }
515 
516 // ===========================================================================
517 
518 GC PixmapPiece::gcp=0;
519 
PixmapPiece()520 PixmapPiece::PixmapPiece() {
521 	tilemap = 0;
522 	if (!gcp)	gcp=XCreateGC(dpy,RootWindow(dpy,scr),0,0);
523 
524 }
~PixmapPiece()525 PixmapPiece::~PixmapPiece() {
526 	DropPixmap();
527 }
DropPixmap()528 void PixmapPiece::DropPixmap() {
529 	if (tilemap) {
530 		XFreePixmap(dpy,tilemap);
531 		tilemap=0;
532 	}
533 }
534 
535 #define DATA_TYPE	CARD32
CreateTilemap32()536 void PixmapPiece::CreateTilemap32() {
537 #	include "rotate.H"
538 }
539 #undef DATA_TYPE
540 
541 #define CARD24 long
542 
543 #define DATA_TYPE	CARD24
CreateTilemap24()544 void PixmapPiece::CreateTilemap24() {
545 #	include "rotate.H"
546 }
547 #undef DATA_TYPE
548 
549 #define DATA_TYPE	CARD16
CreateTilemap16()550 void PixmapPiece::CreateTilemap16() {
551 #	include "rotate.H"
552 }
553 #undef DATA_TYPE
554 
555 #define DATA_TYPE	CARD8
CreateTilemap8()556 void PixmapPiece::CreateTilemap8() {
557 #	include "rotate.H"
558 }
559 #undef DATA_TYPE
560 
561 
DirectionChanged()562 void PixmapPiece::DirectionChanged() {
563 	DropPixmap();
564 	BitmapPiece::DirectionChanged();
565 
566 	tilemap=XCreatePixmap(dpy,RootWindow(dpy,scr),width,height,DefaultDepth(dpy,scr));
567 
568 	if (windir==0&&!itm&&!page) {
569 		Vec2			wcenter;
570 		if (page&&pm->IsTwinPixmap())
571 					wcenter=Vec2( center.X(), pm->XHeight()+center.Y() );
572 		else		wcenter=center;
573 
574 		XCopyArea(dpy,pm->GetPixmap(),tilemap,gcp,
575 			XPix(wcenter.X())-offx, YPix(wcenter.Y())-offy, width, height, 0, 0 );
576 	}
577 	else {
578 		extern int pixmap_depth;
579 
580 		switch(pixmap_depth) {
581 		case 8:		CreateTilemap8();	break;
582 		case 16:	CreateTilemap16();	break;
583 		case 32:	CreateTilemap32();	break;
584 		case 24:	CreateTilemap24();	break;
585 		default:
586 			fprintf(stderr,"unhandled depth = %d\n",pixmap_depth);
587 			exit(1);
588 		}
589 	}
590 }
591 
Redraw()592 void PixmapPiece::Redraw() {
593 // printf( "winpos: %g %g\n", (double)winpos.X(), (double)winpos.Y() );
594 // printf( "offx: %d %d\n", offx, offy );
595 
596 	XSetClipMask(dpy,gc,GetBitmap());
597 	XSetClipOrigin(dpy,gc,winx,winy);
598 
599 	GetPixmap();
600 	XCopyArea(dpy,tilemap,win,gc,
601 		0, 0, width, height, winx, winy );
602 }
603 
604 // ===========================================================================
605 
ShadowedPiece()606 ShadowedPiece::ShadowedPiece() {
607 	shadowmap = 0;
608 	if (!gcp)	gcp=XCreateGC(dpy,RootWindow(dpy,scr),0,0);
609 
610 }
~ShadowedPiece()611 ShadowedPiece::~ShadowedPiece() {
612 	DropPixmap();
613 }
DropPixmap()614 void ShadowedPiece::DropPixmap() {
615 	if (shadowmap) {
616 		XFreePixmap(dpy,shadowmap);
617 		shadowmap=0;
618 		XFreePixmap(dpy,shadowmask);
619 	}
620 }
621 
DirectionChanged()622 void ShadowedPiece::DirectionChanged() {
623 int x;
624 
625 	DropPixmap();
626 	PixmapPiece::DirectionChanged();
627 
628 	swidth = width   + 2*ShadowSize();
629 	sheight = height + 2*ShadowSize();
630 
631 // create mask of the shadowed tile
632 	shadowmask=XCreatePixmap(dpy,RootWindow(dpy,scr),swidth,sheight,1);
633 
634 	XSetForeground(dpy,gcb,0);
635 	XFillRectangle(dpy,shadowmask,gcb,0,0,swidth,sheight);
636 
637 	XSetFunction(dpy,gcb,GXor);
638 	for (x=0;x<=2*ShadowSize();x++) {
639 		XCopyArea(dpy,tilemask,shadowmask,gcb,0,0,width,height,x,x);
640 	}
641 	XSetFunction(dpy,gcb,GXcopy);
642 
643 // create the tile
644 	shadowmap=XCreatePixmap(dpy,RootWindow(dpy,scr),swidth,sheight,DefaultDepth(dpy,scr));
645 
646 	XSetForeground(dpy,gc,WhitePixel(dpy,scr));
647 	XSetClipMask(dpy,gc,tilemask);
648 	for (x=0;x<ShadowSize();x++) {
649 		XSetClipOrigin(dpy,gc,x,x);
650 		XFillRectangle(dpy,shadowmap,gc,x,x,width,height);
651 	}
652 	XSetForeground(dpy,gc,BlackPixel(dpy,scr));
653 	for (x=ShadowSize()+1;x<=2*ShadowSize();x++) {
654 		XSetClipOrigin(dpy,gc,x,x);
655 		XFillRectangle(dpy,shadowmap,gc,x,x,width,height);
656 	}
657 
658 	XSetClipOrigin(dpy,gc,ShadowSize(),ShadowSize());
659 	XCopyArea(dpy,tilemap,shadowmap,gc,
660 		0, 0, width, height, ShadowSize(),ShadowSize() );
661 }
662 
Redraw()663 void ShadowedPiece::Redraw() {
664 	XSetClipMask(dpy,gc,shadowmask);
665 	XSetClipOrigin(dpy,gc,winx,winy);
666 
667 	XCopyArea(dpy,shadowmap,win,gc,
668 		0, 0, swidth, sheight, winx, winy );
669 }
670 
IsInside(int x,int y)671 int ShadowedPiece::IsInside( int x, int y ) {
672 XImage	*help_image;
673 
674 	x -= winx;
675 	y -= winy;
676 	if (x<0 || y<0 || x>=swidth || y >=sheight)	return 0;
677 
678 	help_image=XGetImage(dpy,shadowmask,x,y,1,1,1,XYPixmap);
679 	unsigned long erg=XGetPixel(help_image,0,0);
680 	XDestroyImage(help_image);
681 	return (int)erg;
682 }
683 
684 // ===========================================================================
685 
WindowPiece()686 WindowPiece::WindowPiece() {
687 	swin=0;
688 
689 }
~WindowPiece()690 WindowPiece::~WindowPiece() {
691 	if (swin)		XDestroyWindow( dpy, swin );
692 }
693 
CreateWindow()694 void WindowPiece::CreateWindow() {
695 	XSetWindowAttributes	attrib;
696 
697 	swin=XCreateSimpleWindow(dpy,RootWindow(dpy,scr),
698 			winx,winy,swidth,sheight,0,WhitePixel(dpy,scr), port->AllocNamedColor( "grey50" ) );
699 	attrib.bit_gravity=StaticGravity;
700 	attrib.override_redirect = True;
701 	XChangeWindowAttributes( dpy, swin, CWBitGravity|CWOverrideRedirect, &attrib );
702 	XSelectInput(dpy,swin, ExposureMask|KeyPressMask|EnterWindowMask|ButtonPressMask|ButtonReleaseMask|ButtonMotionMask);
703 }
704 
PositionChanged()705 void WindowPiece::PositionChanged() {
706 	ShadowedPiece::PositionChanged();
707 
708 	if (shapes) {
709 		if (swin)	XMoveWindow( dpy, swin, winx, winy );
710 	}
711 }
712 
DirectionChanged()713 void WindowPiece::DirectionChanged() {
714 
715 	ShadowedPiece::DirectionChanged();
716 
717 	if (shapes) {
718 		if (!swin) {
719 			CreateWindow();
720 			XShapeCombineMask(dpy,swin,0 /*Bounding*/,0,0,shadowmask,0 /*Set*/);
721 			XMapRaised(dpy,swin);
722 		}
723 		else {
724 			XMoveResizeWindow(dpy,swin,winx,winy,swidth,sheight);
725 			XShapeCombineMask(dpy,swin,0 /* Bounding */,0,0,shadowmask,0 /* Set */);
726 		}
727 		Redraw();
728 	}
729 }
730 
Redraw()731 void WindowPiece::Redraw() {
732 	XSetClipMask(dpy,gc,shadowmask);
733 	XSetClipOrigin(dpy,gc,0,0);
734 
735 	XCopyArea(dpy,shadowmap,swin,gc, 0, 0, swidth, sheight, 0, 0 );
736 }
737 
738 // ===========================================================================
739 
PieceObject()740 PieceObject::PieceObject() {
741 }
742 
~PieceObject()743 PieceObject::~PieceObject() {
744 }
745 
Intersects(int x,int y,int w,int h)746 int PieceObject::Intersects(int x,int y,int w,int h) {
747 	if (	(x>winx+swidth) || (winx>x+w)
748 		||	(y>winy+sheight) || (winy>y+h) )		return 0;
749 	else													return 1;
750 }
751 
IsInside(int x,int y)752 int PieceObject::IsInside(int x,int y) {
753 	if ((x>=winx)&&(x<winx+swidth)&&(y>=winy)&&(y<winy+sheight)) {
754 		if (ShadowedPiece::IsInside(x,y))		return 2;	// exact hit
755 		else												return 1;	// somewhere near
756 	}
757 	else													return 0;	// far away
758 }
759 
ExposeRegion(int x,int y,int,int)760 void PieceObject::ExposeRegion(int x,int y,int /*width*/,int /*height*/) {
761 	XSetClipMask(dpy,gc,shadowmask);
762 	XSetClipOrigin(dpy,gc,winx-x, winy-y);
763 	XCopyArea(dpy,shadowmap,mystack->dbmap,gc,
764 				0, 0, swidth, sheight, winx-x, winy-y );
765 }
766 
ExposeWindowRegion(Window w,int,int,int,int)767 void PieceObject::ExposeWindowRegion(Window w,int /*x*/,int /*y*/,int /*width*/,int /*height*/) {
768 	if (w==swin) {
769 		XSetClipMask(dpy,gc,shadowmask);
770 		XSetClipOrigin(dpy,gc,0,0);
771 
772 		XCopyArea(dpy,shadowmap,swin,gc, 0, 0, swidth, sheight, 0, 0 );
773 	}
774 }
775 
PanView(int ox,int oy)776 void PieceObject::PanView( int ox, int oy ) {
777 	winpos-=Vec2(ox,oy);
778 	PositionChanged();
779 }
780 
ZoomView(int midx,int midy,int chg)781 void PieceObject::ZoomView( int midx, int midy, int chg ) {
782 double	factor=((double)(zoom_factor+chg))/zoom_factor;
783 
784 	center = center * factor;
785 	*vl = *vl * factor;
786 	winpos = (winpos-Vec2(midx,midy)) * factor + Vec2(midx,midy);
787 	PositionChanged();
788 	DirectionChanged();
789 }
790 
791 // ===========================================================================
792 
793 int DBPieceObject::x1;
794 int DBPieceObject::y1;
795 int DBPieceObject::x2;
796 int DBPieceObject::y2;
797 
DBPieceObject()798 DBPieceObject::DBPieceObject() {
799 }
800 
~DBPieceObject()801 DBPieceObject::~DBPieceObject() {
802 }
803 
TurnOver(const Real & d)804 void DBPieceObject::TurnOver( const Real &d ) {
805 		XDefineCursor( dpy, win, no_cursor );
806 		XFlush(dpy);
807 
808 Real a;
809 Real start_windir=windir;
810 
811 	a=fmod(d-start_windir+360,360);
812 	if (a>180.0)	a=a-360.0;
813 
814 	if (join_count<=maxturntiles) {
815 		double turnstart=GetCurrentTime()-0.1;
816 		double turntime=turntimebase+join_count*turntimedelta;
817 		double loop;
818 		for ( loop=0.1; loop<0.9; loop=(GetCurrentTime()-turnstart)/turntime) {
819 			StoreExtent();
820 			SetDir( start_windir+loop*a );
821 			UpdateExtent();
822 			XSync(dpy,0);
823 		}
824 	}
825 	StoreExtent();
826 	SetDir(d);
827 	UpdateExtent();
828 }
829 
FlipOver(const Vec2 & pos)830 void DBPieceObject::FlipOver( const Vec2 &pos ) {
831 		XDefineCursor( dpy, win, no_cursor );
832 		XFlush(dpy);
833 
834 Vec2  wpos=pos;						// button position after probable adjustment
835 Real	wa=winpos.AngleDeg(pos);	// angle to button position
836 
837 	if (fmod(windir+5,90)<10) {		// close to straight -> adjusted flip
838 
839 // Probably round position for exact flips
840 // int	quart=(int)(fmod(wa+22,360)/45)&3;
841 int	quart=(int)(fmod(wa+45,360)/45)&2;
842 
843 		switch(quart) {
844 		case 0: {
845 				wpos=Vec2( pos.X(), winpos.Y() );			// horizontal flip
846 				break;
847 			}
848 		case 1: {
849 				Vec2	erg;											// diagonal flip 1
850 				(pos-winpos).Split( Vec2(1,-1), &erg );	// project to diagonal
851 				wpos=winpos+erg;
852 				break;
853 			}
854 		case 2: {
855 				wpos=Vec2( winpos.X(), pos.Y() );			// vertival flip
856 				break;
857 			}
858 		case 3: {
859 				Vec2	erg;											// diagonal flip 2
860 				(pos-winpos).Split( Vec2(1,1), &erg );		// project to diagonal
861 				wpos=winpos+erg;
862 				break;
863 			}
864 		}
865 		wa=winpos.AngleDeg(wpos);		// angle to (adjusted position)
866 	}
867 
868 
869 Vec2	mdir=wpos-winpos;					// move distance to center of flip
870 Vec2	newpos=mdir+wpos;					// destination on other side of the flip
871 Real	a=wa+90;								// angle of mirror
872 Real	newdir=a-(windir-a)+180;				// new windir after the flip
873 
874 	DBG1( "*** Mirror Angle: %g\n", (double)a );
875 	DBG2( "*** Windir: %g -> New Angle: %g\n", (double)wa, (double)newdir );
876 
877 	if (join_count<=maxfliptiles) {
878 	// do tiny animation, if there are less than 5 tiles combined
879 
880 		Real	flipstart=GetCurrentTime();
881 		Real	loop;				// current state of the flip
882 
883 		double fliptime=fliptimebase+join_count*fliptimedelta;
884 		StartFlip( Vec2Zero.AngleRadial(mdir) );// start flipping up
885 		for (loop=0.9;loop>=flipsave;loop=1.0-(GetCurrentTime()-flipstart)/fliptime) {
886 			StoreExtent();
887 			SetPos( wpos-loop*mdir );
888 			SetFlip( loop );
889 			UpdateExtent();
890 			XSync(dpy,0);
891 		}
892 
893 		StoreExtent();									// swap to other side
894 		FlipPage();
895 		SetDir(newdir);
896 
897 		loop=flipsave;									// first frame on back side
898 		flipstart=GetCurrentTime();
899 		StartFlip( Vec2Zero.AngleRadial(wpos-newpos) );
900 		SetPos( wpos+loop*mdir );
901 		SetFlip(loop);
902 		UpdateExtent();
903 		XSync(dpy,0);
904 		loop=flipsave+(GetCurrentTime()-flipstart)/fliptime;
905 
906 		for (;loop<1.0-0.1;loop=flipsave+(GetCurrentTime()-flipstart)/fliptime) {
907 			StoreExtent();
908 			SetPos( wpos+loop*mdir );
909 			SetFlip( loop );
910 			UpdateExtent();
911 			XSync(dpy,0);
912 		}
913 
914 		StoreExtent();									// last frame on back side
915 		SetPos(newpos);
916 		StopFlip();
917 		DirectionChanged();							// trigger for a last update
918 		UpdateExtent();
919 	}
920 	else {
921 		StoreExtent();									// just flip and show
922 		FlipPage();
923 		SetPos(newpos);
924 		SetDir(newdir);
925 		UpdateExtent();
926 	}
927 }
928 
StoreExtent()929 void DBPieceObject::StoreExtent() {
930 	x1 = winx;					// store old frame
931 	y1 = winy;
932 	x2 = x1+swidth;
933 	y2 = y1+sheight;
934 }
935 
JoinExtent(int * xx1,int * yy1,int * xx2,int * yy2)936 int DBPieceObject::JoinExtent( int *xx1, int *yy1, int *xx2, int *yy2 ) {
937 	if (winx<*xx1)				*xx1 = winx;
938 	if (winy<*yy1)				*yy1 = winy;
939 	if (winx+swidth>*xx2)	*xx2 = winx+swidth;
940 	if (winy+sheight>*yy2)	*yy2 = winy+sheight;
941 	return 1;
942 }
943 
GetExtent(int * xx1,int * yy1,int * xx2,int * yy2)944 int DBPieceObject::GetExtent( int *xx1, int *yy1, int *xx2, int *yy2 ) {
945 	*xx1 = winx;
946 	*yy1 = winy;
947 	*xx2 = winx+swidth;
948 	*yy2 = winy+sheight;
949 	return 1;
950 }
951 
JoinExtent()952 void DBPieceObject::JoinExtent() {
953 	if (winx<x1)			x1=winx;
954 	if (winx+swidth>x2)	x2=winx+swidth;
955 	if (winy<y1)			y1=winy;
956 	if (winy+sheight>y2)	y2=winy+sheight;
957 }
958 
UpdateExtent()959 void DBPieceObject::UpdateExtent() {
960 	JoinExtent();
961 	mystack->ExposeRegion(x1,y1,x2-x1,y2-y1);
962 }
963 
964 // ===========================================================================
965 
966 int  MoveablePiece::turnflag;			// flag about direction of turn
967 Time MoveablePiece::start_time;		// event time of button press
968 Real MoveablePiece::start_angle;		// angle at the start of turn
969 Vec2 MoveablePiece::start;
970 Vec2 MoveablePiece::poffset;			// offset of pointer from center
971 Real MoveablePiece::poffset_len;		// length of offset of pointer from center
972 
MoveablePiece()973 MoveablePiece::MoveablePiece() {
974 }
975 
~MoveablePiece()976 MoveablePiece::~MoveablePiece() {
977 }
978 
DispatchPress(XButtonEvent * xbutton)979 void MoveablePiece::DispatchPress( XButtonEvent * xbutton ) {
980 	if (xbutton->state&ControlMask) {
981 		// printf( "doing the flip ...\n" );
982 		mystack->Raise(this);
983 		FlipOver( Vec2(xbutton->x,xbutton->y) );
984 		start_time=0;
985 	}
986 	else {
987 		start_time=xbutton->time;
988 	}
989 
990 	start_angle=GetDir();
991 	if (xbutton->state&AnyButtonMask) {
992 		// there is already a button pressed ...
993 		if (xbutton->button==Button1) {
994 			// second button in addition to middle button -> turn around center
995 			turnflag=2*(xbutton->button-Button1-1);
996 		}
997 		else {
998 			// start non-rotating motion on multiple press
999 			turnflag=0;
1000 		}
1001 	}
1002 	else {
1003 		// first button - start normal drag
1004 		mystack->Raise(this);
1005 		start=Vec2( xbutton->x, xbutton->y );
1006 		poffset=(start-winpos);
1007 		poffset_len=poffset.Norm();
1008 
1009 		// when too close to the center of the tile - move it off a bit ...
1010 		while (poffset_len<2) {
1011 			// printf( "*** very close hit - moving start\n" );
1012 			start+=Vec2(1,1);
1013 			poffset=(start-winpos);
1014 			poffset_len=poffset.Norm();
1015 		}
1016 
1017 		turnflag=xbutton->button-Button1-1;
1018 
1019 		if (poffset_len<warp_center) {
1020 			poffset_len=warp_center;
1021 			poffset=poffset_len*poffset.Norm1();
1022 			start=winpos+poffset;
1023 			warp_lock_x = XPix(start.X());
1024 			warp_lock_y = YPix(start.Y());
1025 			XWarpPointer(dpy,None,win,0,0,0,0,warp_lock_x,warp_lock_y);
1026 			XSync(dpy,0);		// Flush and drop the Warp-Event
1027 		}
1028 
1029 		if (!turnflag)		Move( start-poffset );
1030 	}
1031 	XDefineCursor( dpy, win, (turnflag<0)?pull_cursor:move_cursor );
1032 }
1033 
DispatchMotion(XMotionEvent * xmotion)1034 void MoveablePiece::DispatchMotion( XMotionEvent * xmotion ) {
1035 
1036 Vec2	newpos(xmotion->x,xmotion->y);
1037 Vec2	new_offset=newpos-(start-poffset);
1038 
1039 	if (fabs(new_offset.X())+fabs(new_offset.Y())<2) {
1040 		DBG0( "#### skipping event near center of tile\n" );
1041 		return;
1042 	}
1043 
1044 	switch (turnflag) {
1045 	case 2:
1046 	case -2: {
1047 		// new style for turning around center
1048 			Real	a1=Vec2Zero.AngleDeg(poffset);
1049 			Real	a2=Vec2Zero.AngleDeg(new_offset);
1050 			poffset=new_offset;
1051 			MoveTurn(newpos-poffset,GetDir()-a1+a2);
1052 			break;
1053 	}
1054 	case -1: {
1055 		// new style for pulling corners
1056 			new_offset=(new_offset).Norm1()*poffset_len;
1057 			Real	a1=Vec2Zero.AngleDeg(poffset);
1058 			Real	a2=Vec2Zero.AngleDeg(new_offset);
1059 			poffset=new_offset;
1060 			MoveTurn(newpos-poffset,GetDir()-a1+a2);
1061 			break;
1062 	}
1063 	case 1:
1064 	default:
1065 			Move( newpos-poffset );
1066 			break;
1067 	}
1068 	start=newpos;
1069 }
1070 
DispatchRelease(XButtonEvent * xbutton)1071 void MoveablePiece::DispatchRelease( XButtonEvent * xbutton ) {
1072 	if (xbutton->button!=Button2&&(xbutton->button==Button1||!(xbutton->state&Button1Mask))) {
1073 			if (xbutton->time-start_time<150) {
1074 				switch(xbutton->button) {
1075 				case Button1:	TurnOver(start_angle+90.0);	break;
1076 				case Button3:	TurnOver(start_angle-90.0);	break;
1077 				}
1078 			}
1079 			turnflag=0;
1080 	}
1081 	else if (xbutton->time-start_time<150&&xbutton->button==Button2&&!(xbutton->state&~Button2Mask)) {
1082 				FlipOver(Vec2(xbutton->x,xbutton->y));
1083 	}
1084 	else if (xbutton->state&Button1Mask)
1085 			turnflag=-1;	// restart rotatable drag
1086 
1087 	if (!((xbutton->state&AnyButtonMask)&~(Button1Mask<<(xbutton->button-1)))) {
1088 		XDefineCursor( dpy, win, idle_cursor );
1089 		XFlush(dpy);
1090 		// no more buttons pressed -> check against other pieces
1091 		if (p->CheckForJoin( (Piece*)this )) {
1092 			/* don't use THIS in this case since it's already deleted !!! */
1093 		}
1094 		else {
1095 			AdjustDirection();
1096 		}
1097 	}
1098 	else XDefineCursor( dpy, win, (turnflag)?pull_cursor:move_cursor );
1099 }
1100 
1101