#ifndef _global_h # include "global.h" #endif #include #include #ifndef _objects_h # include "objects.H" #endif #ifndef _gifx_image_h # include "gifx_image.H" #endif #ifndef _imgbuff_h # include "imgbuff.H" #endif #ifndef _vec2_h # include "vec2.h" #endif #ifndef _vec2list_h # include "vec2list.h" #endif #ifndef _mat2_h # include "mat2.h" #endif #ifndef _color_mapper_h # include "color_mapper.H" #endif #ifndef _puzzle_h # include "puzzle.H" #endif /****************************************************************************** PieceFrame - corner, edge and pin-data of the piece including polygon-spline Vec2List *vl RotatedFrame - position-data including window-position, upward-angle and rotated polygin spline Vec2 winpos Real windir Vec2List *tvl BitmapPiece - pixmap with mask of the rotated Piece Pixmap tilemask PixmapPiece - pixmap with Puzzle-Image of the rotated Piece Pixmap tilemap ShadowPiece - pixmap with Puzzle-Image and ShadowFrame of the rotated Piece Pixmap shadowmask Pixmap shadowmap PieceObject - object subclass to be stack-controlled DBPieceObject - double buffered moves and turns ******************************************************************************/ // . . . . + . . . . + . . . . + . . . . // . . . . . . . . . . . . . . . . . . . // . * . . . . . . . . . . . . . . . . . // . . . . . . . . . . . . . . . . . . . // . . . . . . . . . . . . . . . . . . . // . . . . + . . . . + . . . . + . . . . // . . . . . . . . . . . . . . . . . . . // . * . . . . . . . . . . . . . . . . . // . . . . . . . . . * . . * . . . . . . // . . * . . . . . . . . . . . . . . . . // . . . . * . * . . + . . . . + * . . . // . . . . . . . . . . . . . . . . . . . // . . . . . . . . . . . . . . . . . . . // . . . . . . . . . . . . . . . . . . . // . . . . . . . . . . . . . . . . . * . // . . . . + . . . . + . . . . + . . . . // . . . . . . . . . . . . . . . . . * . double PieceFrameSetup::spx[] = { 1, 2.5, 5, 7, 7, 5, 5, 6, 8,13 }; double PieceFrameSetup::spy[] = { 13,12.5,11, 8, 5, 2, 0,-2,-3,-3 }; PieceFrameSetup::PieceFrameSetup() { } PieceFrameSetup::~PieceFrameSetup() { } void PieceFrameSetup::Init( const Vec2 &tl, const Vec2 &tr, const Vec2 &br, const Vec2 &bl ) { center = (tl+tr+br+bl)/4; v[0] = tl-center; v[1] = tr-center; v[2] = br-center; v[3] = bl-center; pin[0] = pin[1] = pin[2] = pin[3] = 0; #if (0) printf( "%g %g %g %g\n", (double)tl.X(), (double)tl.Y(), (double)tr.X(), (double)tr.Y() ); printf( "center: %g %g\n", (double)center.X(), (double)center.Y() ); printf( "%g %g %g %g\n", (double)bl.X(), (double)bl.Y(), (double)br.X(), (double)br.Y() ); #endif } void PieceFrame::PositionChanged() {} void PieceFrame::DirectionChanged() {} PieceFrame::PieceFrame() { vl = 0; join_count=1; } PieceFrame::~PieceFrame() { if (vl) delete vl; } const Vec2List &PieceFrame::Init( const PieceFrameSetup &pfs ) { center=pfs.center; vl = new Vec2List(MaxSplineLen); for (int i=0;i<4;i++) { Vec2 s(pfs.v[i]); if (pfs.pin[i]) { int j; Vec2 e(pfs.v[(i+1)%4]); // end of segment Vec2 d=e-s; // edge vector Vec2 m1=s+pfs.pin[i]*d; // medium point for pin Vec2 bw=(s-m1)/24.0; // backstep vector to startpoint Vec2 fw=(e-m1)/24.0; // forward vector to endpoint Vec2 n=d/44; Vec2 o=(pfs.left[i]?n.TurnLeft():n.TurnRight()); *vl |= s; for (j=1;j<=HalfSplineLen;j++) { *vl |= m1 + pfs.spx[HalfSplineLen-j]*bw + pfs.spy[HalfSplineLen-j]*o; } for (j=0;jwindir; // check for correct page if (page!=obj->page) return 0; // check for an angle between -10 and +10 degrees if (fmod(windir_delta+380.0,360.0)>40) return 0; Vec2 v1(center - obj->center); Vec2 v2(winpos - obj->winpos); Real help=fabs(windir-obj->windir); Real mid_angle; if (help>180.0) mid_angle=fmod((windir+obj->windir+360.0)/2,360.0); else mid_angle=fmod((windir+obj->windir)/2,360.0); Vec2 erg; v2=v2.TurnAngleDeg( -mid_angle ); if (page) { erg=Vec2(v1.X()+v2.X(),v1.Y()-v2.Y()); } else erg=Vec2(v1.X()-v2.X(),v1.Y()-v2.Y()); #ifdef DEBUG v2=v2.TurnAngleDeg( mid_angle ); printf( "ImageA: %6.2f %6.2f WindowA: %6.2f %6.2f at angle: %6.2f\n", (double)center.X(), (double)center.Y(), (double)winpos.X(), (double)winpos.Y(), (double)windir ); printf( "ImageB: %6.2f %6.2f WindowB: %6.2f %6.2f at angle: %6.2f\n", (double)obj->center.X(), (double)obj->center.Y(), (double)obj->winpos.X(), (double)obj->winpos.Y(), (double)obj->windir ); printf( " %6.2f %6.2f %6.2f %6.2f\n", (double)v1.X(), (double)v1.Y(), (double)v2.X(), (double)v2.Y() ); v2=v2.TurnAngleDeg( -mid_angle ); printf( " angle: %6.2f %6.2f %6.2f\n", mid_angle, (double)v2.X(), (double)v2.Y() ); printf( " %6.2f %6.2f\n", (double)erg.X(), (double)erg.Y() ); #endif if (erg.Norm()Len()); for (int i=0;iLen();i++) { tvl->AddAt(i,Vec2( -(*vl)[i].X(), (*vl)[i].Y() )); } } tvl->TurnAngleDeg(windir); } // DoJoin: // A Joined segment in both polylines has to be found. When found // the points out of that segment are added from the second to the first // polyline, which will result into the merge of the two outlines. // // vl: +----+----+----+----+----+----+----+---- // 1 2 3 4 s j e 7 8 // obj->vl: +----+----+----+---- // 1' e i s 4' // result: +----+----+----+----+----+----+----+----+----+---- // 1 2 3 4 s 4' 1' e 7 8 // // special cases: // obj->vl: +----+----+----+---- // 1' 2' i s 4' // e int RotatedFrame::FindStartForJoin( class PieceFrame *obj ) { int i,j; for (i=0;ivl->Len();i++) { for (j=0;jLen();j++) { if (same_point(j,obj,i)) return i; } } return -1; } int RotatedFrame::DoJoin( class RotatedFrame *obj, int i, int swap ) { int j,k; for (j=0;jLen();j++) { if (same_point(j,obj,i)) { // found intersecting point: now find start and end of intersection int s,e; int sj; int ej; int c=1; for (s=1;svl->Len();s++) { int si=(i+s)%obj->vl->Len(); sj=((j-s)+vl->Len())%vl->Len(); if (!same_point(sj,obj,si)) break; c++; } s--; sj=((j-s)+vl->Len())%vl->Len(); for (e=1;evl->Len();e++) { int ei=((i-e)+obj->vl->Len())%obj->vl->Len(); ej=(j+e)%vl->Len(); if (!same_point(ej,obj,ei)) break; c++; } e--; ej=(j+e)%vl->Len(); // found start and end of intersection as in shown in diagramm // now the points have to be moved to the destination polygon DBG2( "*** Range: %d %d\n", sj, ej ); if ((sj==ej)&&(c>1)) { // // when a surrounding tile is moved onto of the inner tile, the // problem arise that the whole inner tile and the partial surrounding // tile has to be deleted. Since this usually happens the other way round: // "The hole is filled with the inner tile.", this special case is reversed // by swapping both tiles and redoing the join, in which the partial list // of the surrounding tile will be found in sj - ej // DBG0( "*** special inner case: do reverse join\n" ); Vec2 help_center=center; center=obj->center; obj->center=help_center; Vec2List *help_list=vl; vl=obj->vl; obj->vl=help_list; return DoJoin(obj,j,1); } join_count+=obj->join_count; vl->DelRange(sj,ej,&sj); k=(-e+obj->vl->Len())%obj->vl->Len(); if ( k!=s || (sj==ej&&c==1) ) { DBG1( "*** Insert at: %d -", sj ); do { DBG1( " %d", (i+k)%obj->vl->Len() ); vl->AddAt(sj+1,(*obj->vl)(i+k)+obj->center-center); k=(k-1+obj->vl->Len())%obj->vl->Len(); } while( k!=s ); DBG0( "\n" ); } else { // an inner tile was removed from the polyline and therefore // some kind of one way trail to the inner tile remained in the // polyline which should be removed with a tiny "garbage" collector. DBG1( "*** Garbage Deleted (from: %d)", vl->Len() ); for (j=0;jLen()-2;j++) { while (same_point(j,this,j+2)) { DBG1( "%d ", j ); vl->DelRange(j,j+2,&j); if (j>0) j--; } } DBG1( " (to: %d)\n", vl->Len() ); } // the new extend has to be queried to setup the center Vec2 tl, br; vl->GetExtent( &tl, &br ); Vec2 new_center( center+((tl+br)/2) ); #if (0) printf( " center: %g %g\n", center.X(), center.Y() ); printf( " from: %g %g\n", tl.X(), tl.Y() ); printf( " to: %g %g\n", br.X(), br.Y() ); printf( "new center: %g %g\n", new_center.X(), new_center.Y() ); #endif if (swap) { // this time the second objects position should be remained winpos = obj->winpos-(obj->center-new_center).ScaleX(page?-1:1).TurnAngleDeg(obj->windir); } else winpos -= (center-new_center).ScaleX(page?-1:1).TurnAngleDeg(windir); *vl += (center-new_center); center=new_center; PositionChanged(); DirectionChanged(); return 1; } } return 0; } // =========================================================================== FlipFrame::FlipFrame() { ftvl=0; itm=0; } FlipFrame::~FlipFrame() { if (ftvl) delete ftvl; if (itm) delete itm; } void FlipFrame::StartFlip( const Real &angle ) { mangle = angle; } void FlipFrame::StopFlip() { if (ftvl) delete ftvl; ftvl=0; if (itm) delete itm; itm=0; } void FlipFrame::SetFlip( const Real ¤t ) { if (ftvl) delete ftvl; if (itm) delete itm; itm = new Mat2(); // set up transformation if (page) *itm = (*itm).ScaleX( -1 ); *itm = (*itm).RotateDeg( -windir ) .Rotate( mangle ) .ScaleX( current ) .Rotate( -mangle ); ftvl = new Vec2List(RotatedFrame::GetPolyLine(),*itm); *itm = !*itm; // reverse transformation for texture-mapping DirectionChanged(); } Vec2List &FlipFrame::GetTPolyLine() { if (!ftvl) return *tvl; else return *ftvl; } // =========================================================================== GC BitmapPiece::gcb = 0; BitmapPiece::BitmapPiece() { tilemask = 0; } BitmapPiece::~BitmapPiece() { DropBitmap(); } void BitmapPiece::DropBitmap() { if (tilemask) { XFreePixmap(dpy,tilemask); tilemask=0; } } void BitmapPiece::PositionChanged() { winx = XPix(winpos.X())-offx; winy = YPix(winpos.Y())-offy; } void BitmapPiece::DirectionChanged() { DropBitmap(); FlipFrame::DirectionChanged(); Vec2 tl,br; int x1,y1,x2,y2; const Vec2List &poly( GetTPolyLine() ); poly.GetExtent( &tl, &br ); x1 = XPix(tl.X()); y1 = YPix(tl.Y()); x2 = XPix(br.X()); y2 = YPix(br.Y()); offx = -x1; offy = -y1; winx = XPix(winpos.X())-offx; winy = YPix(winpos.Y())-offy; width = x2-x1+1; height = y2-y1+1; #if (0) printf( "%g %g (%d %d)\n", (double)tl.X(), (double)tl.Y(), offx, offy ); printf( "center: %g %g\n", (double)center.X(), (double)center.Y() ); printf( " %g %g\n", (double)br.X(), (double)br.Y() ); #endif tilemask=XCreatePixmap(dpy,RootWindow(dpy,scr),width,height,1); if (!gcb) gcb=XCreateGC(dpy,tilemask,0,0); XSetForeground(dpy,gcb,0); // clear the new bitmap XFillRectangle(dpy,tilemask,gcb,0,0,width,height); XSetForeground(dpy,gcb,1); // draw the polyline XPoint xpts[MaxSplineLen]; XPoint *xpts_p; if ((unsigned)poly.Len()>(sizeof(xpts)/sizeof(XPoint))) { xpts_p=new XPoint[poly.Len()]; // create buffer when necessary } else xpts_p=xpts; for (int i=0;iIsTwinPixmap()) wcenter=Vec2( center.X(), pm->XHeight()+center.Y() ); else wcenter=center; XCopyArea(dpy,pm->GetPixmap(),tilemap,gcp, XPix(wcenter.X())-offx, YPix(wcenter.Y())-offy, width, height, 0, 0 ); } else { extern int pixmap_depth; switch(pixmap_depth) { case 8: CreateTilemap8(); break; case 16: CreateTilemap16(); break; case 32: CreateTilemap32(); break; case 24: CreateTilemap24(); break; default: fprintf(stderr,"unhandled depth = %d\n",pixmap_depth); exit(1); } } } void PixmapPiece::Redraw() { // printf( "winpos: %g %g\n", (double)winpos.X(), (double)winpos.Y() ); // printf( "offx: %d %d\n", offx, offy ); XSetClipMask(dpy,gc,GetBitmap()); XSetClipOrigin(dpy,gc,winx,winy); GetPixmap(); XCopyArea(dpy,tilemap,win,gc, 0, 0, width, height, winx, winy ); } // =========================================================================== ShadowedPiece::ShadowedPiece() { shadowmap = 0; if (!gcp) gcp=XCreateGC(dpy,RootWindow(dpy,scr),0,0); } ShadowedPiece::~ShadowedPiece() { DropPixmap(); } void ShadowedPiece::DropPixmap() { if (shadowmap) { XFreePixmap(dpy,shadowmap); shadowmap=0; XFreePixmap(dpy,shadowmask); } } void ShadowedPiece::DirectionChanged() { int x; DropPixmap(); PixmapPiece::DirectionChanged(); swidth = width + 2*ShadowSize(); sheight = height + 2*ShadowSize(); // create mask of the shadowed tile shadowmask=XCreatePixmap(dpy,RootWindow(dpy,scr),swidth,sheight,1); XSetForeground(dpy,gcb,0); XFillRectangle(dpy,shadowmask,gcb,0,0,swidth,sheight); XSetFunction(dpy,gcb,GXor); for (x=0;x<=2*ShadowSize();x++) { XCopyArea(dpy,tilemask,shadowmask,gcb,0,0,width,height,x,x); } XSetFunction(dpy,gcb,GXcopy); // create the tile shadowmap=XCreatePixmap(dpy,RootWindow(dpy,scr),swidth,sheight,DefaultDepth(dpy,scr)); XSetForeground(dpy,gc,WhitePixel(dpy,scr)); XSetClipMask(dpy,gc,tilemask); for (x=0;x=swidth || y >=sheight) return 0; help_image=XGetImage(dpy,shadowmask,x,y,1,1,1,XYPixmap); unsigned long erg=XGetPixel(help_image,0,0); XDestroyImage(help_image); return (int)erg; } // =========================================================================== WindowPiece::WindowPiece() { swin=0; } WindowPiece::~WindowPiece() { if (swin) XDestroyWindow( dpy, swin ); } void WindowPiece::CreateWindow() { XSetWindowAttributes attrib; swin=XCreateSimpleWindow(dpy,RootWindow(dpy,scr), winx,winy,swidth,sheight,0,WhitePixel(dpy,scr), port->AllocNamedColor( "grey50" ) ); attrib.bit_gravity=StaticGravity; attrib.override_redirect = True; XChangeWindowAttributes( dpy, swin, CWBitGravity|CWOverrideRedirect, &attrib ); XSelectInput(dpy,swin, ExposureMask|KeyPressMask|EnterWindowMask|ButtonPressMask|ButtonReleaseMask|ButtonMotionMask); } void WindowPiece::PositionChanged() { ShadowedPiece::PositionChanged(); if (shapes) { if (swin) XMoveWindow( dpy, swin, winx, winy ); } } void WindowPiece::DirectionChanged() { ShadowedPiece::DirectionChanged(); if (shapes) { if (!swin) { CreateWindow(); XShapeCombineMask(dpy,swin,0 /*Bounding*/,0,0,shadowmask,0 /*Set*/); XMapRaised(dpy,swin); } else { XMoveResizeWindow(dpy,swin,winx,winy,swidth,sheight); XShapeCombineMask(dpy,swin,0 /* Bounding */,0,0,shadowmask,0 /* Set */); } Redraw(); } } void WindowPiece::Redraw() { XSetClipMask(dpy,gc,shadowmask); XSetClipOrigin(dpy,gc,0,0); XCopyArea(dpy,shadowmap,swin,gc, 0, 0, swidth, sheight, 0, 0 ); } // =========================================================================== PieceObject::PieceObject() { } PieceObject::~PieceObject() { } int PieceObject::Intersects(int x,int y,int w,int h) { if ( (x>winx+swidth) || (winx>x+w) || (y>winy+sheight) || (winy>y+h) ) return 0; else return 1; } int PieceObject::IsInside(int x,int y) { if ((x>=winx)&&(x=winy)&&(ydbmap,gc, 0, 0, swidth, sheight, winx-x, winy-y ); } void PieceObject::ExposeWindowRegion(Window w,int /*x*/,int /*y*/,int /*width*/,int /*height*/) { if (w==swin) { XSetClipMask(dpy,gc,shadowmask); XSetClipOrigin(dpy,gc,0,0); XCopyArea(dpy,shadowmap,swin,gc, 0, 0, swidth, sheight, 0, 0 ); } } void PieceObject::PanView( int ox, int oy ) { winpos-=Vec2(ox,oy); PositionChanged(); } void PieceObject::ZoomView( int midx, int midy, int chg ) { double factor=((double)(zoom_factor+chg))/zoom_factor; center = center * factor; *vl = *vl * factor; winpos = (winpos-Vec2(midx,midy)) * factor + Vec2(midx,midy); PositionChanged(); DirectionChanged(); } // =========================================================================== int DBPieceObject::x1; int DBPieceObject::y1; int DBPieceObject::x2; int DBPieceObject::y2; DBPieceObject::DBPieceObject() { } DBPieceObject::~DBPieceObject() { } void DBPieceObject::TurnOver( const Real &d ) { XDefineCursor( dpy, win, no_cursor ); XFlush(dpy); Real a; Real start_windir=windir; a=fmod(d-start_windir+360,360); if (a>180.0) a=a-360.0; if (join_count<=maxturntiles) { double turnstart=GetCurrentTime()-0.1; double turntime=turntimebase+join_count*turntimedelta; double loop; for ( loop=0.1; loop<0.9; loop=(GetCurrentTime()-turnstart)/turntime) { StoreExtent(); SetDir( start_windir+loop*a ); UpdateExtent(); XSync(dpy,0); } } StoreExtent(); SetDir(d); UpdateExtent(); } void DBPieceObject::FlipOver( const Vec2 &pos ) { XDefineCursor( dpy, win, no_cursor ); XFlush(dpy); Vec2 wpos=pos; // button position after probable adjustment Real wa=winpos.AngleDeg(pos); // angle to button position if (fmod(windir+5,90)<10) { // close to straight -> adjusted flip // Probably round position for exact flips // int quart=(int)(fmod(wa+22,360)/45)&3; int quart=(int)(fmod(wa+45,360)/45)&2; switch(quart) { case 0: { wpos=Vec2( pos.X(), winpos.Y() ); // horizontal flip break; } case 1: { Vec2 erg; // diagonal flip 1 (pos-winpos).Split( Vec2(1,-1), &erg ); // project to diagonal wpos=winpos+erg; break; } case 2: { wpos=Vec2( winpos.X(), pos.Y() ); // vertival flip break; } case 3: { Vec2 erg; // diagonal flip 2 (pos-winpos).Split( Vec2(1,1), &erg ); // project to diagonal wpos=winpos+erg; break; } } wa=winpos.AngleDeg(wpos); // angle to (adjusted position) } Vec2 mdir=wpos-winpos; // move distance to center of flip Vec2 newpos=mdir+wpos; // destination on other side of the flip Real a=wa+90; // angle of mirror Real newdir=a-(windir-a)+180; // new windir after the flip DBG1( "*** Mirror Angle: %g\n", (double)a ); DBG2( "*** Windir: %g -> New Angle: %g\n", (double)wa, (double)newdir ); if (join_count<=maxfliptiles) { // do tiny animation, if there are less than 5 tiles combined Real flipstart=GetCurrentTime(); Real loop; // current state of the flip double fliptime=fliptimebase+join_count*fliptimedelta; StartFlip( Vec2Zero.AngleRadial(mdir) );// start flipping up for (loop=0.9;loop>=flipsave;loop=1.0-(GetCurrentTime()-flipstart)/fliptime) { StoreExtent(); SetPos( wpos-loop*mdir ); SetFlip( loop ); UpdateExtent(); XSync(dpy,0); } StoreExtent(); // swap to other side FlipPage(); SetDir(newdir); loop=flipsave; // first frame on back side flipstart=GetCurrentTime(); StartFlip( Vec2Zero.AngleRadial(wpos-newpos) ); SetPos( wpos+loop*mdir ); SetFlip(loop); UpdateExtent(); XSync(dpy,0); loop=flipsave+(GetCurrentTime()-flipstart)/fliptime; for (;loop<1.0-0.1;loop=flipsave+(GetCurrentTime()-flipstart)/fliptime) { StoreExtent(); SetPos( wpos+loop*mdir ); SetFlip( loop ); UpdateExtent(); XSync(dpy,0); } StoreExtent(); // last frame on back side SetPos(newpos); StopFlip(); DirectionChanged(); // trigger for a last update UpdateExtent(); } else { StoreExtent(); // just flip and show FlipPage(); SetPos(newpos); SetDir(newdir); UpdateExtent(); } } void DBPieceObject::StoreExtent() { x1 = winx; // store old frame y1 = winy; x2 = x1+swidth; y2 = y1+sheight; } int DBPieceObject::JoinExtent( int *xx1, int *yy1, int *xx2, int *yy2 ) { if (winx<*xx1) *xx1 = winx; if (winy<*yy1) *yy1 = winy; if (winx+swidth>*xx2) *xx2 = winx+swidth; if (winy+sheight>*yy2) *yy2 = winy+sheight; return 1; } int DBPieceObject::GetExtent( int *xx1, int *yy1, int *xx2, int *yy2 ) { *xx1 = winx; *yy1 = winy; *xx2 = winx+swidth; *yy2 = winy+sheight; return 1; } void DBPieceObject::JoinExtent() { if (winxx2) x2=winx+swidth; if (winyy2) y2=winy+sheight; } void DBPieceObject::UpdateExtent() { JoinExtent(); mystack->ExposeRegion(x1,y1,x2-x1,y2-y1); } // =========================================================================== int MoveablePiece::turnflag; // flag about direction of turn Time MoveablePiece::start_time; // event time of button press Real MoveablePiece::start_angle; // angle at the start of turn Vec2 MoveablePiece::start; Vec2 MoveablePiece::poffset; // offset of pointer from center Real MoveablePiece::poffset_len; // length of offset of pointer from center MoveablePiece::MoveablePiece() { } MoveablePiece::~MoveablePiece() { } void MoveablePiece::DispatchPress( XButtonEvent * xbutton ) { if (xbutton->state&ControlMask) { // printf( "doing the flip ...\n" ); mystack->Raise(this); FlipOver( Vec2(xbutton->x,xbutton->y) ); start_time=0; } else { start_time=xbutton->time; } start_angle=GetDir(); if (xbutton->state&AnyButtonMask) { // there is already a button pressed ... if (xbutton->button==Button1) { // second button in addition to middle button -> turn around center turnflag=2*(xbutton->button-Button1-1); } else { // start non-rotating motion on multiple press turnflag=0; } } else { // first button - start normal drag mystack->Raise(this); start=Vec2( xbutton->x, xbutton->y ); poffset=(start-winpos); poffset_len=poffset.Norm(); // when too close to the center of the tile - move it off a bit ... while (poffset_len<2) { // printf( "*** very close hit - moving start\n" ); start+=Vec2(1,1); poffset=(start-winpos); poffset_len=poffset.Norm(); } turnflag=xbutton->button-Button1-1; if (poffset_lenx,xmotion->y); Vec2 new_offset=newpos-(start-poffset); if (fabs(new_offset.X())+fabs(new_offset.Y())<2) { DBG0( "#### skipping event near center of tile\n" ); return; } switch (turnflag) { case 2: case -2: { // new style for turning around center Real a1=Vec2Zero.AngleDeg(poffset); Real a2=Vec2Zero.AngleDeg(new_offset); poffset=new_offset; MoveTurn(newpos-poffset,GetDir()-a1+a2); break; } case -1: { // new style for pulling corners new_offset=(new_offset).Norm1()*poffset_len; Real a1=Vec2Zero.AngleDeg(poffset); Real a2=Vec2Zero.AngleDeg(new_offset); poffset=new_offset; MoveTurn(newpos-poffset,GetDir()-a1+a2); break; } case 1: default: Move( newpos-poffset ); break; } start=newpos; } void MoveablePiece::DispatchRelease( XButtonEvent * xbutton ) { if (xbutton->button!=Button2&&(xbutton->button==Button1||!(xbutton->state&Button1Mask))) { if (xbutton->time-start_time<150) { switch(xbutton->button) { case Button1: TurnOver(start_angle+90.0); break; case Button3: TurnOver(start_angle-90.0); break; } } turnflag=0; } else if (xbutton->time-start_time<150&&xbutton->button==Button2&&!(xbutton->state&~Button2Mask)) { FlipOver(Vec2(xbutton->x,xbutton->y)); } else if (xbutton->state&Button1Mask) turnflag=-1; // restart rotatable drag if (!((xbutton->state&AnyButtonMask)&~(Button1Mask<<(xbutton->button-1)))) { XDefineCursor( dpy, win, idle_cursor ); XFlush(dpy); // no more buttons pressed -> check against other pieces if (p->CheckForJoin( (Piece*)this )) { /* don't use THIS in this case since it's already deleted !!! */ } else { AdjustDirection(); } } else XDefineCursor( dpy, win, (turnflag)?pull_cursor:move_cursor ); }