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 ¤t ) {
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