pen-commits Mailing List for Puzzles from the Engineer's Notebook (Page 2)
Brought to you by:
mdgeorge
You can subscribe to this list here.
2008 |
Jan
|
Feb
|
Mar
|
Apr
|
May
|
Jun
|
Jul
|
Aug
|
Sep
|
Oct
|
Nov
(7) |
Dec
(29) |
---|
From: <mdg...@us...> - 2008-12-03 01:01:54
|
Revision: 106 http://pen.svn.sourceforge.net/pen/?rev=106&view=rev Author: mdgeorge Date: 2008-12-03 01:01:53 +0000 (Wed, 03 Dec 2008) Log Message: ----------- Removed some more symlinks Removed Paths: ------------- COPYING INSTALL Deleted: COPYING =================================================================== --- COPYING 2008-12-03 00:46:08 UTC (rev 105) +++ COPYING 2008-12-03 01:01:53 UTC (rev 106) @@ -1 +0,0 @@ -link /usr/share/automake-1.9/COPYING \ No newline at end of file Deleted: INSTALL =================================================================== --- INSTALL 2008-12-03 00:46:08 UTC (rev 105) +++ INSTALL 2008-12-03 01:01:53 UTC (rev 106) @@ -1 +0,0 @@ -link /usr/share/automake-1.9/INSTALL \ No newline at end of file This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <mdg...@us...> - 2008-12-03 00:46:11
|
Revision: 105 http://pen.svn.sourceforge.net/pen/?rev=105&view=rev Author: mdgeorge Date: 2008-12-03 00:46:08 +0000 (Wed, 03 Dec 2008) Log Message: ----------- Replacing symlinks with copies. Added Paths: ----------- pygame/src/bitmask.c pygame/src/bitmask.h pygame/src/mask.c pygame/src/mask.doc pygame/src/pygamedocs.h pygame/test/mask_test.py Added: pygame/src/bitmask.c =================================================================== --- pygame/src/bitmask.c (rev 0) +++ pygame/src/bitmask.c 2008-12-03 00:46:08 UTC (rev 105) @@ -0,0 +1,1038 @@ +/* + Bitmask Collision Detection Library 1.5 + Copyright (C) 2002-2005 Ulf Ekstrom except for the bitcount function which + is copyright (C) Donald W. Gillies, 1992, and the other bitcount function + which was taken from Jorg Arndt's excellent "Algorithms for Programmers" + text. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <stdlib.h> +#include <stddef.h> +#include <string.h> +#include "bitmask.h" + +#ifndef INLINE +#warning No INLINE definition in bitmask.h, performance may suffer. +#endif + +#define MIN(a,b) ((a) <= (b) ? (a) : (b)) +#define MAX(a,b) ((a) >= (b) ? (a) : (b)) + +/* The code by Gillies is slightly (1-3%) faster than the more + readable code below */ +#define GILLIES + +static INLINE unsigned int bitcount(BITMASK_W n) +{ + if (BITMASK_W_LEN == 32) + { +#ifdef GILLIES +/* (C) Donald W. Gillies, 1992. All rights reserved. You may reuse + this bitcount() function anywhere you please as long as you retain + this Copyright Notice. */ + register unsigned long tmp; + return (tmp = (n) - (((n) >> 1) & 033333333333) - + (((n) >> 2) & 011111111111), + tmp = ((tmp + (tmp >> 3)) & 030707070707), + tmp = (tmp + (tmp >> 6)), + tmp = (tmp + (tmp >> 12) + (tmp >> 24)) & 077); +/* End of Donald W. Gillies bitcount code */ +#else + /* This piece taken from Jorg Arndt's "Algorithms for Programmers" */ + n = ((n>>1) & 0x55555555) + (n & 0x55555555); // 0-2 in 2 bits + n = ((n>>2) & 0x33333333) + (n & 0x33333333); // 0-4 in 4 bits + n = ((n>>4) + n) & 0x0f0f0f0f; // 0-8 in 4 bits + n += n>> 8; // 0-16 in 8 bits + n += n>>16; // 0-32 in 8 bits + return n & 0xff; +#endif + } + else if (BITMASK_W_LEN == 64) + { + n = ((n>>1) & 0x5555555555555555) + (n & 0x5555555555555555); + n = ((n>>2) & 0x3333333333333333) + (n & 0x3333333333333333); + n = ((n>>4) + n) & 0x0f0f0f0f0f0f0f0f; + n += n>> 8; + n += n>>16; + n += n>>32; + return n & 0xff; + } + else + { + /* Handle non-32 or 64 bit case the slow way */ + unsigned int nbits = 0; + while (n) + { + if (n & 1) + nbits++; + n = n >> 1; + } + return nbits; + } +} + +bitmask_t *bitmask_create(int w, int h) +{ + bitmask_t *temp; + temp = malloc(offsetof(bitmask_t,bits) + h*((w - 1)/BITMASK_W_LEN + 1)*sizeof(BITMASK_W)); + if (!temp) + return 0; + temp->w = w; + temp->h = h; + bitmask_clear(temp); + return temp; +} + +void bitmask_free(bitmask_t *m) +{ + free(m); +} + +void bitmask_clear(bitmask_t *m) +{ + memset(m->bits,0,m->h*((m->w - 1)/BITMASK_W_LEN + 1)*sizeof(BITMASK_W)); +} + +void bitmask_fill(bitmask_t *m) +{ + int len, shift; + BITMASK_W *pixels, cmask, full; + + len = m->h*((m->w - 1)/BITMASK_W_LEN); + shift = BITMASK_W_LEN - (m->w % BITMASK_W_LEN); + full = ~(BITMASK_W)0; + cmask = (~(BITMASK_W)0) >> shift; + /* fill all the pixels that aren't in the rightmost BITMASK_Ws */ + for (pixels = m->bits; pixels < (m->bits + len); pixels++) { + *pixels = full; + } + /* for the rightmost BITMASK_Ws, use cmask to ensure we aren't setting + bits that are outside of the mask */ + for (pixels = m->bits + len; pixels < (m->bits + len + m->h); pixels++) { + *pixels = cmask; + } +} + +void bitmask_invert(bitmask_t *m) +{ + int len, shift; + BITMASK_W *pixels, cmask; + + len = m->h*((m->w - 1)/BITMASK_W_LEN); + shift = BITMASK_W_LEN - (m->w % BITMASK_W_LEN); + cmask = (~(BITMASK_W)0) >> shift; + /* flip all the pixels that aren't in the rightmost BITMASK_Ws */ + for (pixels = m->bits; pixels < (m->bits + len); pixels++) { + *pixels = ~(*pixels); + } + /* for the rightmost BITMASK_Ws, & with cmask to ensure we aren't setting + bits that are outside of the mask */ + for (pixels = m->bits + len; pixels < (m->bits + len + m->h); pixels++) { + *pixels = cmask & ~(*pixels); + } +} + +unsigned int bitmask_count(bitmask_t *m) +{ + BITMASK_W *pixels; + unsigned int tot = 0; + + for (pixels=m->bits; pixels<(m->bits+m->h*((m->w-1)/BITMASK_W_LEN + 1)); pixels++) { + tot += bitcount(*pixels); + } + + return tot; +} + +int bitmask_overlap(const bitmask_t *a, const bitmask_t *b, int xoffset, int yoffset) +{ + const BITMASK_W *a_entry,*a_end; + const BITMASK_W *b_entry; + const BITMASK_W *ap,*app,*bp; + unsigned int shift,rshift,i,astripes,bstripes; + + if ((xoffset >= a->w) || (yoffset >= a->h) || (b->h + yoffset <= 0) || (b->w + xoffset <= 0)) + return 0; + + if (xoffset >= 0) + { + swapentry: + if (yoffset >= 0) + { + a_entry = a->bits + a->h*((unsigned int)xoffset/BITMASK_W_LEN) + yoffset; + a_end = a_entry + MIN(b->h,a->h - yoffset); + b_entry = b->bits; + } + else + { + a_entry = a->bits + a->h*((unsigned int)xoffset/BITMASK_W_LEN); + a_end = a_entry + MIN(b->h + yoffset,a->h); + b_entry = b->bits - yoffset; + } + shift = xoffset & BITMASK_W_MASK; + if (shift) + { + rshift = BITMASK_W_LEN - shift; + astripes = ((unsigned int)(a->w - 1))/BITMASK_W_LEN - (unsigned int)xoffset/BITMASK_W_LEN; + bstripes = ((unsigned int)(b->w - 1))/BITMASK_W_LEN + 1; + if (bstripes > astripes) /* zig-zag .. zig*/ + { + for (i=0;i<astripes;i++) + { + for (ap = a_entry, app = ap + a->h, bp = b_entry;ap < a_end;) + if ((*ap++ >> shift) & *bp || (*app++ << rshift) & *bp++) return 1; + a_entry += a->h; + a_end += a->h; + b_entry += b->h; + } + for (ap = a_entry,bp = b_entry;ap < a_end;) + if ((*ap++ >> shift) & *bp++) return 1; + return 0; + } + else /* zig-zag */ + { + for (i=0;i<bstripes;i++) + { + for (ap = a_entry, app = ap + a->h, bp = b_entry;ap < a_end;) + if ((*ap++ >> shift) & *bp || (*app++ << rshift) & *bp++) return 1; + a_entry += a->h; + a_end += a->h; + b_entry += b->h; + } + return 0; + } + } + else /* xoffset is a multiple of the stripe width, and the above routines wont work */ + { + astripes = (MIN(b->w,a->w - xoffset) - 1)/BITMASK_W_LEN + 1; + for (i=0;i<astripes;i++) + { + for (ap = a_entry,bp = b_entry;ap < a_end;) + if (*ap++ & *bp++) return 1; + a_entry += a->h; + a_end += a->h; + b_entry += b->h; + } + return 0; + } + } + else + { + const bitmask_t *c = a; + a = b; + b = c; + xoffset *= -1; + yoffset *= -1; + goto swapentry; + } +} + +/* Will hang if there are no bits set in w! */ +static INLINE int firstsetbit(BITMASK_W w) +{ + int i = 0; + while ((w & 1) == 0) + { + i++; + w/=2; + } + return i; +} + +/* x and y are given in the coordinates of mask a, and are untouched if there is no overlap */ +int bitmask_overlap_pos(const bitmask_t *a, const bitmask_t *b, int xoffset, int yoffset, int *x, int *y) +{ + const BITMASK_W *a_entry,*a_end, *b_entry, *ap, *bp; + unsigned int shift,rshift,i,astripes,bstripes,xbase; + + if ((xoffset >= a->w) || (yoffset >= a->h) || (yoffset <= - b->h)) + return 0; + + if (xoffset >= 0) + { + xbase = xoffset/BITMASK_W_LEN; /* first stripe from mask a */ + if (yoffset >= 0) + { + a_entry = a->bits + a->h*xbase + yoffset; + a_end = a_entry + MIN(b->h,a->h - yoffset); + b_entry = b->bits; + } + else + { + a_entry = a->bits + a->h*xbase; + a_end = a_entry + MIN(b->h + yoffset,a->h); + b_entry = b->bits - yoffset; + yoffset = 0; /* relied on below */ + } + shift = xoffset & BITMASK_W_MASK; + if (shift) + { + rshift = BITMASK_W_LEN - shift; + astripes = (a->w - 1)/BITMASK_W_LEN - xoffset/BITMASK_W_LEN; + bstripes = (b->w - 1)/BITMASK_W_LEN + 1; + if (bstripes > astripes) /* zig-zag .. zig*/ + { + for (i=0;i<astripes;i++) + { + for (ap = a_entry,bp = b_entry;ap < a_end;ap++,bp++) + if (*ap & (*bp << shift)) + { + *y = ap - a_entry + yoffset; + *x = (xbase + i)*BITMASK_W_LEN + firstsetbit(*ap & (*bp << shift)); + return 1; + } + a_entry += a->h; + a_end += a->h; + for (ap = a_entry,bp = b_entry;ap < a_end;ap++,bp++) + if (*ap & (*bp >> rshift)) + { + *y = ap - a_entry + yoffset; + *x = (xbase + i + 1)*BITMASK_W_LEN + firstsetbit(*ap & (*bp >> rshift)); + return 1; + } + b_entry += b->h; + } + for (ap = a_entry,bp = b_entry;ap < a_end;ap++,bp++) + if (*ap & (*bp << shift)) + { + *y = ap - a_entry + yoffset; + *x = (xbase + astripes)*BITMASK_W_LEN + firstsetbit(*ap & (*bp << shift)); + return 1; + } + return 0; + } + else /* zig-zag */ + { + for (i=0;i<bstripes;i++) + { + for (ap = a_entry,bp = b_entry;ap < a_end;ap++,bp++) + if (*ap & (*bp << shift)) + { + *y = ap - a_entry + yoffset; + *x = (xbase + i)*BITMASK_W_LEN + firstsetbit(*ap & (*bp << shift)); + return 1; + } + a_entry += a->h; + a_end += a->h; + for (ap = a_entry,bp = b_entry;ap < a_end;ap++,bp++) + if (*ap & (*bp >> rshift)) + { + *y = ap - a_entry + yoffset; + *x = (xbase + i + 1)*BITMASK_W_LEN + firstsetbit(*ap & (*bp >> rshift)); + return 1; + } + b_entry += b->h; + } + return 0; + } + } + else +/* xoffset is a multiple of the stripe width, and the above routines + won't work. This way is also slightly faster. */ + { + astripes = (MIN(b->w,a->w - xoffset) - 1)/BITMASK_W_LEN + 1; + for (i=0;i<astripes;i++) + { + for (ap = a_entry,bp = b_entry;ap < a_end;ap++,bp++) + { + if (*ap & *bp) + { + *y = ap - a_entry + yoffset; + *x = (xbase + i)*BITMASK_W_LEN + firstsetbit(*ap & *bp); + return 1; + } + } + a_entry += a->h; + a_end += a->h; + b_entry += b->h; + } + return 0; + } + } + else + { + if (bitmask_overlap_pos(b,a,-xoffset,-yoffset,x,y)) + { + *x += xoffset; + *y += yoffset; + return 1; + } + else + return 0; + } +} + +int bitmask_overlap_area(const bitmask_t *a, const bitmask_t *b, int xoffset, int yoffset) +{ + const BITMASK_W *a_entry,*a_end, *b_entry, *ap,*bp; + unsigned int shift,rshift,i,astripes,bstripes; + unsigned int count = 0; + + if ((xoffset >= a->w) || (yoffset >= a->h) || (b->h + yoffset <= 0) || (b->w + xoffset <= 0)) + return 0; + + if (xoffset >= 0) + { + swapentry: + if (yoffset >= 0) + { + a_entry = a->bits + a->h*(xoffset/BITMASK_W_LEN) + yoffset; + a_end = a_entry + MIN(b->h,a->h - yoffset); + b_entry = b->bits; + } + else + { + a_entry = a->bits + a->h*(xoffset/BITMASK_W_LEN); + a_end = a_entry + MIN(b->h + yoffset,a->h); + b_entry = b->bits - yoffset; + } + shift = xoffset & BITMASK_W_MASK; + if (shift) + { + rshift = BITMASK_W_LEN - shift; + astripes = (a->w - 1)/BITMASK_W_LEN - xoffset/BITMASK_W_LEN; + bstripes = (b->w - 1)/BITMASK_W_LEN + 1; + if (bstripes > astripes) /* zig-zag .. zig*/ + { + for (i=0;i<astripes;i++) + { + for (ap = a_entry,bp = b_entry;ap < a_end;ap++,bp++) + count += bitcount(((*ap >> shift) | (*(ap + a->h) << rshift)) & *bp); + a_entry += a->h; + a_end += a->h; + b_entry += b->h; + } + for (ap = a_entry,bp = b_entry;ap < a_end;) + count += bitcount((*ap++ >> shift) & *bp++); + return count; + } + else /* zig-zag */ + { + for (i=0;i<bstripes;i++) + { + for (ap = a_entry,bp = b_entry;ap < a_end;ap++,bp++) + count += bitcount(((*ap >> shift) | (*(ap + a->h) << rshift)) & *bp); + a_entry += a->h; + a_end += a->h; + b_entry += b->h; + } + return count; + } + } + else /* xoffset is a multiple of the stripe width, and the above routines wont work */ + { + astripes = (MIN(b->w,a->w - xoffset) - 1)/BITMASK_W_LEN + 1; + for (i=0;i<astripes;i++) + { + for (ap = a_entry,bp = b_entry;ap < a_end;) + count += bitcount(*ap++ & *bp++); + + a_entry += a->h; + a_end += a->h; + b_entry += b->h; + } + return count; + } + } + else + { + const bitmask_t *c = a; + a = b; + b = c; + xoffset *= -1; + yoffset *= -1; + goto swapentry; + } +} + +/* Makes a mask of the overlap of two other masks */ +void bitmask_overlap_mask(const bitmask_t *a, const bitmask_t *b, bitmask_t *c, int xoffset, int yoffset) +{ + const BITMASK_W *a_entry,*a_end, *ap; + const BITMASK_W *b_entry, *b_end, *bp; + BITMASK_W *c_entry, *c_end, *cp; + int shift,rshift,i,astripes,bstripes; + + if ((xoffset >= a->w) || (yoffset >= a->h) || (yoffset <= - b->h)) + return; + + if (xoffset >= 0) + { + if (yoffset >= 0) + { + a_entry = a->bits + a->h*(xoffset/BITMASK_W_LEN) + yoffset; + c_entry = c->bits + c->h*(xoffset/BITMASK_W_LEN) + yoffset; + a_end = a_entry + MIN(b->h,a->h - yoffset); + b_entry = b->bits; + } + else + { + a_entry = a->bits + a->h*(xoffset/BITMASK_W_LEN); + c_entry = c->bits + c->h*(xoffset/BITMASK_W_LEN); + a_end = a_entry + MIN(b->h + yoffset,a->h); + b_entry = b->bits - yoffset; + } + shift = xoffset & BITMASK_W_MASK; + if (shift) + { + rshift = BITMASK_W_LEN - shift; + astripes = (a->w - 1)/BITMASK_W_LEN - xoffset/BITMASK_W_LEN; + bstripes = (b->w - 1)/BITMASK_W_LEN + 1; + if (bstripes > astripes) /* zig-zag .. zig*/ + { + for (i=0;i<astripes;i++) + { + for (ap = a_entry,bp = b_entry,cp = c_entry;ap < a_end;ap++,bp++,cp++) + *cp = *ap & (*bp << shift); + a_entry += a->h; + c_entry += c->h; + a_end += a->h; + for (ap = a_entry,bp = b_entry,cp = c_entry;ap < a_end;ap++,bp++,cp++) + *cp = *ap & (*bp >> rshift); + b_entry += b->h; + } + for (ap = a_entry,bp = b_entry,cp = c_entry;ap < a_end;ap++,bp++,cp++) + *cp = *ap & (*bp << shift); + } + else /* zig-zag */ + { + for (i=0;i<bstripes;i++) + { + for (ap = a_entry,bp = b_entry,cp = c_entry;ap < a_end;ap++,bp++,cp++) + *cp = *ap & (*bp << shift); + a_entry += a->h; + c_entry += c->h; + a_end += a->h; + for (ap = a_entry,bp = b_entry,cp = c_entry;ap < a_end;ap++,bp++,cp++) + *cp = *ap & (*bp >> rshift); + b_entry += b->h; + } + } + } + else /* xoffset is a multiple of the stripe width, + and the above routines won't work. */ + { + astripes = (MIN(b->w,a->w - xoffset) - 1)/BITMASK_W_LEN + 1; + for (i=0;i<astripes;i++) + { + for (ap = a_entry,bp = b_entry,cp = c_entry;ap < a_end;ap++,bp++,cp++) + { + *cp = *ap & *bp; + } + a_entry += a->h; + c_entry += c->h; + a_end += a->h; + b_entry += b->h; + } + } + } + else + { + xoffset *= -1; + yoffset *= -1; + + if (yoffset >= 0) + { + b_entry = b->bits + b->h*(xoffset/BITMASK_W_LEN) + yoffset; + b_end = b_entry + MIN(a->h,b->h - yoffset); + a_entry = a->bits; + c_entry = c->bits; + } + else + { + b_entry = b->bits + b->h*(xoffset/BITMASK_W_LEN); + b_end = b_entry + MIN(a->h + yoffset,b->h); + a_entry = a->bits - yoffset; + c_entry = c->bits - yoffset; + } + shift = xoffset & BITMASK_W_MASK; + if (shift) + { + rshift = BITMASK_W_LEN - shift; + astripes = (b->w - 1)/BITMASK_W_LEN - xoffset/BITMASK_W_LEN; + bstripes = (a->w - 1)/BITMASK_W_LEN + 1; + if (bstripes > astripes) /* zig-zag .. zig*/ + { + for (i=0;i<astripes;i++) + { + for (bp = b_entry,ap = a_entry,cp = c_entry;bp < b_end;bp++,ap++,cp++) + *cp = *ap & (*bp >> shift); + b_entry += b->h; + b_end += b->h; + for (bp = b_entry,ap = a_entry,cp = c_entry;bp < b_end;bp++,ap++,cp++) + *cp = *ap & (*bp <<rshift); + a_entry += a->h; + c_entry += c->h; + } + for (bp = b_entry,ap = a_entry,cp = c_entry;bp < b_end;bp++,ap++,cp++) + *cp = *ap & (*bp >> shift); + } + else /* zig-zag */ + { + for (i=0;i<bstripes;i++) + { + for (bp = b_entry,ap = a_entry,cp = c_entry;bp < b_end;bp++,ap++,cp++) + *cp = *ap & (*bp >> shift); + b_entry += b->h; + b_end += b->h; + for (bp = b_entry,ap = a_entry,cp = c_entry;bp < b_end;bp++,ap++,cp++) + *cp = *ap & (*bp << rshift); + a_entry += a->h; + c_entry += c->h; + } + } + } + else /* xoffset is a multiple of the stripe width, and the above routines won't work. */ + { + astripes = (MIN(a->w,b->w - xoffset) - 1)/BITMASK_W_LEN + 1; + for (i=0;i<astripes;i++) + { + for (bp = b_entry,ap = a_entry,cp = c_entry;bp < b_end;bp++,ap++,cp++) + { + *cp = *ap & *bp; + } + b_entry += b->h; + b_end += b->h; + a_entry += a->h; + c_entry += c->h; + } + } + xoffset *= -1; + yoffset *= -1; + } + /* Zero out bits outside the mask rectangle (to the right), if there + is a chance we were drawing there. */ + if (xoffset + b->w > c->w) + { + BITMASK_W edgemask; + int n = c->w/BITMASK_W_LEN; + shift = (n + 1)*BITMASK_W_LEN - c->w; + edgemask = (~(BITMASK_W)0) >> shift; + c_end = c->bits + n*c->h + MIN(c->h,b->h + yoffset); + for (cp = c->bits + n*c->h + MAX(yoffset,0);cp<c_end;cp++) + *cp &= edgemask; + } +} + +/* Draws mask b onto mask a (bitwise OR) */ +void bitmask_draw(bitmask_t *a, const bitmask_t *b, int xoffset, int yoffset) +{ + BITMASK_W *a_entry,*a_end, *ap; + const BITMASK_W *b_entry, *b_end, *bp; + int shift,rshift,i,astripes,bstripes; + + if ((xoffset >= a->w) || (yoffset >= a->h) || (yoffset <= - b->h)) + return; + + if (xoffset >= 0) + { + if (yoffset >= 0) + { + a_entry = a->bits + a->h*(xoffset/BITMASK_W_LEN) + yoffset; + a_end = a_entry + MIN(b->h,a->h - yoffset); + b_entry = b->bits; + } + else + { + a_entry = a->bits + a->h*(xoffset/BITMASK_W_LEN); + a_end = a_entry + MIN(b->h + yoffset,a->h); + b_entry = b->bits - yoffset; + } + shift = xoffset & BITMASK_W_MASK; + if (shift) + { + rshift = BITMASK_W_LEN - shift; + astripes = (a->w - 1)/BITMASK_W_LEN - xoffset/BITMASK_W_LEN; + bstripes = (b->w - 1)/BITMASK_W_LEN + 1; + if (bstripes > astripes) /* zig-zag .. zig*/ + { + for (i=0;i<astripes;i++) + { + for (ap = a_entry,bp = b_entry;ap < a_end;ap++,bp++) + *ap |= (*bp << shift); + a_entry += a->h; + a_end += a->h; + for (ap = a_entry,bp = b_entry;ap < a_end;ap++,bp++) + *ap |= (*bp >> rshift); + b_entry += b->h; + } + for (ap = a_entry,bp = b_entry;ap < a_end;ap++,bp++) + *ap |= (*bp << shift); + } + else /* zig-zag */ + { + for (i=0;i<bstripes;i++) + { + for (ap = a_entry,bp = b_entry;ap < a_end;ap++,bp++) + *ap |= (*bp << shift); + a_entry += a->h; + a_end += a->h; + for (ap = a_entry,bp = b_entry;ap < a_end;ap++,bp++) + *ap |= (*bp >> rshift); + b_entry += b->h; + } + } + } + else /* xoffset is a multiple of the stripe width, + and the above routines won't work. */ + { + astripes = (MIN(b->w,a->w - xoffset) - 1)/BITMASK_W_LEN + 1; + for (i=0;i<astripes;i++) + { + for (ap = a_entry,bp = b_entry;ap < a_end;ap++,bp++) + { + *ap |= *bp; + } + a_entry += a->h; + a_end += a->h; + b_entry += b->h; + } + } + } + else + { + xoffset *= -1; + yoffset *= -1; + + if (yoffset >= 0) + { + b_entry = b->bits + b->h*(xoffset/BITMASK_W_LEN) + yoffset; + b_end = b_entry + MIN(a->h,b->h - yoffset); + a_entry = a->bits; + } + else + { + b_entry = b->bits + b->h*(xoffset/BITMASK_W_LEN); + b_end = b_entry + MIN(a->h + yoffset,b->h); + a_entry = a->bits - yoffset; + } + shift = xoffset & BITMASK_W_MASK; + if (shift) + { + rshift = BITMASK_W_LEN - shift; + astripes = (b->w - 1)/BITMASK_W_LEN - xoffset/BITMASK_W_LEN; + bstripes = (a->w - 1)/BITMASK_W_LEN + 1; + if (bstripes > astripes) /* zig-zag .. zig*/ + { + for (i=0;i<astripes;i++) + { + for (bp = b_entry,ap = a_entry;bp < b_end;bp++,ap++) + *ap |= (*bp >> shift); + b_entry += b->h; + b_end += b->h; + for (bp = b_entry,ap = a_entry;bp < b_end;bp++,ap++) + *ap |= (*bp <<rshift); + a_entry += a->h; + } + for (bp = b_entry,ap = a_entry;bp < b_end;bp++,ap++) + *ap |= (*bp >> shift); + } + else /* zig-zag */ + { + for (i=0;i<bstripes;i++) + { + for (bp = b_entry,ap = a_entry;bp < b_end;bp++,ap++) + *ap |= (*bp >> shift); + b_entry += b->h; + b_end += b->h; + for (bp = b_entry,ap = a_entry;bp < b_end;bp++,ap++) + *ap |= (*bp << rshift); + a_entry += a->h; + } + } + } + else /* xoffset is a multiple of the stripe width, and the above routines won't work. */ + { + astripes = (MIN(a->w,b->w - xoffset) - 1)/BITMASK_W_LEN + 1; + for (i=0;i<astripes;i++) + { + for (bp = b_entry,ap = a_entry;bp < b_end;bp++,ap++) + { + *ap |= *bp; + } + b_entry += b->h; + b_end += b->h; + a_entry += a->h; + } + } + xoffset *= -1; + yoffset *= -1; + } + /* Zero out bits outside the mask rectangle (to the right), if there + is a chance we were drawing there. */ + if (xoffset + b->w > a->w) + { + BITMASK_W edgemask; + int n = a->w/BITMASK_W_LEN; + shift = (n + 1)*BITMASK_W_LEN - a->w; + edgemask = (~(BITMASK_W)0) >> shift; + a_end = a->bits + n*a->h + MIN(a->h,b->h + yoffset); + for (ap = a->bits + n*a->h + MAX(yoffset,0);ap<a_end;ap++) + *ap &= edgemask; + } +} + +/* Erases mask b from mask a (a &= ~b) */ +void bitmask_erase(bitmask_t *a, const bitmask_t *b, int xoffset, int yoffset) +{ + BITMASK_W *a_entry,*a_end, *ap; + const BITMASK_W *b_entry, *b_end, *bp; + int shift,rshift,i,astripes,bstripes; + + if ((xoffset >= a->w) || (yoffset >= a->h) || (yoffset <= - b->h)) + return; + + if (xoffset >= 0) + { + if (yoffset >= 0) + { + a_entry = a->bits + a->h*(xoffset/BITMASK_W_LEN) + yoffset; + a_end = a_entry + MIN(b->h,a->h - yoffset); + b_entry = b->bits; + } + else + { + a_entry = a->bits + a->h*(xoffset/BITMASK_W_LEN); + a_end = a_entry + MIN(b->h + yoffset,a->h); + b_entry = b->bits - yoffset; + } + shift = xoffset & BITMASK_W_MASK; + if (shift) + { + rshift = BITMASK_W_LEN - shift; + astripes = (a->w - 1)/BITMASK_W_LEN - xoffset/BITMASK_W_LEN; + bstripes = (b->w - 1)/BITMASK_W_LEN + 1; + if (bstripes > astripes) /* zig-zag .. zig*/ + { + for (i=0;i<astripes;i++) + { + for (ap = a_entry,bp = b_entry;ap < a_end;ap++,bp++) + *ap &= ~(*bp << shift); + a_entry += a->h; + a_end += a->h; + for (ap = a_entry,bp = b_entry;ap < a_end;ap++,bp++) + *ap &= ~(*bp >> rshift); + b_entry += b->h; + } + for (ap = a_entry,bp = b_entry;ap < a_end;ap++,bp++) + *ap &= ~(*bp << shift); + } + else /* zig-zag */ + { + for (i=0;i<bstripes;i++) + { + for (ap = a_entry,bp = b_entry;ap < a_end;ap++,bp++) + *ap &= ~(*bp << shift); + a_entry += a->h; + a_end += a->h; + for (ap = a_entry,bp = b_entry;ap < a_end;ap++,bp++) + *ap &= ~(*bp >> rshift); + b_entry += b->h; + } + } + } + else /* xoffset is a multiple of the stripe width, + and the above routines won't work. */ + { + astripes = (MIN(b->w,a->w - xoffset) - 1)/BITMASK_W_LEN + 1; + for (i=0;i<astripes;i++) + { + for (ap = a_entry,bp = b_entry;ap < a_end;ap++,bp++) + { + *ap &= ~*bp; + } + a_entry += a->h; + a_end += a->h; + b_entry += b->h; + } + } + } + else + { + xoffset *= -1; + yoffset *= -1; + + if (yoffset >= 0) + { + b_entry = b->bits + b->h*(xoffset/BITMASK_W_LEN) + yoffset; + b_end = b_entry + MIN(a->h,b->h - yoffset); + a_entry = a->bits; + } + else + { + b_entry = b->bits + b->h*(xoffset/BITMASK_W_LEN); + b_end = b_entry + MIN(a->h + yoffset,b->h); + a_entry = a->bits - yoffset; + } + shift = xoffset & BITMASK_W_MASK; + if (shift) + { + rshift = BITMASK_W_LEN - shift; + astripes = (b->w - 1)/BITMASK_W_LEN - xoffset/BITMASK_W_LEN; + bstripes = (a->w - 1)/BITMASK_W_LEN + 1; + if (bstripes > astripes) /* zig-zag .. zig*/ + { + for (i=0;i<astripes;i++) + { + for (bp = b_entry,ap = a_entry;bp < b_end;bp++,ap++) + *ap &= ~(*bp >> shift); + b_entry += b->h; + b_end += b->h; + for (bp = b_entry,ap = a_entry;bp < b_end;bp++,ap++) + *ap &= ~(*bp <<rshift); + a_entry += a->h; + } + for (bp = b_entry,ap = a_entry;bp < b_end;bp++,ap++) + *ap |= (*bp >> shift); + } + else /* zig-zag */ + { + for (i=0;i<bstripes;i++) + { + for (bp = b_entry,ap = a_entry;bp < b_end;bp++,ap++) + *ap &= ~(*bp >> shift); + b_entry += b->h; + b_end += b->h; + for (bp = b_entry,ap = a_entry;bp < b_end;bp++,ap++) + *ap &= ~(*bp << rshift); + a_entry += a->h; + } + } + } + else /* xoffset is a multiple of the stripe width, and the above routines won't work. */ + { + astripes = (MIN(a->w,b->w - xoffset) - 1)/BITMASK_W_LEN + 1; + for (i=0;i<astripes;i++) + { + for (bp = b_entry,ap = a_entry;bp < b_end;bp++,ap++) + *ap &= ~*bp; + b_entry += b->h; + b_end += b->h; + a_entry += a->h; + } + } + } +} + + + +bitmask_t *bitmask_scale(const bitmask_t *m, int w, int h) +{ + bitmask_t *nm; + int x,y,nx,ny,dx,dy,dnx,dny; + + if (w < 1 || h < 1) + { + nm = bitmask_create(1,1); + return nm; + } + + nm = bitmask_create(w,h); + if (!nm) + return NULL; + ny = dny = 0; + for (y=0,dy=h; y<m->h; y++,dy+=h) + { + while (dny < dy) + { + nx = dnx = 0; + for (x=0,dx=w; x < m->w; x++, dx+=w) + { + if (bitmask_getbit(m,x,y)) + { + while (dnx < dx) + { + bitmask_setbit(nm,nx,ny); + nx++; + dnx += m->w; + } + } + else + { + while (dnx < dx) + { + nx++; + dnx += m->w; + } + } + } + ny++; + dny+=m->h; + } + } + return nm; +} + +void bitmask_convolve(const bitmask_t *a, const bitmask_t *b, bitmask_t *o, int xoffset, int yoffset) +{ + int x, y; + + xoffset += b->w - 1; + yoffset += b->h - 1; + for (y = 0; y < b->h; y++) + for (x = 0; x < b->w; x++) + if (bitmask_getbit(b, x, y)) + bitmask_draw(o, a, xoffset - x, yoffset - y); +} + +static inline int valid(const bitmask_t *mask, int x, int y) +{ + return x < 0 || y < 0 || + x >= mask->w || y >= mask->h || + !bitmask_getbit(mask,x,y); +} + +void bitmask_find_closest(const bitmask_t *mask, int *x, int *y) +{ +#define SQ(x) (x)*(x) + + /* this method is either very elegant or very ugly. */ + int best_d = SQ(*x + 1), best_x = -1, best_y = *y; + int covered; + +#define check_point(dist,x,y) \ + if(dist < best_d) { \ + best_d = dist; \ + best_x = x; \ + best_y = y; \ + } + + if (valid(mask, *x, *y)) + check_point(0, *x, *y); + + /* loop invariant: I've at least looked at everything of distance less than less than covered */ + for (covered = 1; SQ(covered) <= best_d; covered++) + { + int sx, sy, i; + +#define check_row(xi, yi) \ + sx = *x - (xi)*covered + (yi)*covered; \ + sy = *y - (yi)*covered - (xi)*covered; \ + for (i = 0; i < 2*covered; i++, sx += xi, sy += yi) \ + if (valid(mask, sx, sy)) \ + check_point(SQ(*x-sx) + SQ(*y-sy), sx, sy); + + check_row(1,0); + check_row(-1,0); + check_row(0,1); + check_row(0,-1); + } + + *x = best_x; *y = best_y; +} + + Added: pygame/src/bitmask.h =================================================================== --- pygame/src/bitmask.h (rev 0) +++ pygame/src/bitmask.h 2008-12-03 00:46:08 UTC (rev 105) @@ -0,0 +1,149 @@ +/* + Bitmask 1.7 - A pixel-perfect collision detection library. + + Copyright (C) 2002-2005 Ulf Ekstrom except for the bitcount + function which is copyright (C) Donald W. Gillies, 1992. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef BITMASK_H +#define BITMASK_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include <limits.h> +/* Define INLINE for different compilers. If your compiler does not + support inlining then there might be a performance hit in + bitmask_overlap_area(). +*/ +#ifndef INLINE +# ifdef __GNUC__ +# define INLINE inline +# else +# ifdef _MSC_VER +# define INLINE __inline +# else +# define INLINE +# endif +# endif +#endif + +#define BITMASK_W unsigned long int +#define BITMASK_W_LEN (sizeof(BITMASK_W)*CHAR_BIT) +#define BITMASK_W_MASK (BITMASK_W_LEN - 1) +#define BITMASK_N(n) ((BITMASK_W)1 << (n)) + +typedef struct bitmask +{ + int w,h; + BITMASK_W bits[1]; +} bitmask_t; + +/* Creates a bitmask of width w and height h, where + w and h must both be greater than 0. + The mask is automatically cleared when created. + */ +bitmask_t *bitmask_create(int w, int h); + +/* Frees all the memory allocated by bitmask_create for m. */ +void bitmask_free(bitmask_t *m); + +/* Clears all bits in the mask */ +void bitmask_clear(bitmask_t *m); + +/* Sets all bits in the mask */ +void bitmask_fill(bitmask_t *m); + +/* Flips all bits in the mask */ +void bitmask_invert(bitmask_t *m); + +/* Counts the bits in the mask */ +unsigned int bitmask_count(bitmask_t *m); + +/* Returns nonzero if the bit at (x,y) is set. Coordinates start at + (0,0) */ +static INLINE int bitmask_getbit(const bitmask_t *m, int x, int y) +{ + return (m->bits[x/BITMASK_W_LEN*m->h + y] & BITMASK_N(x & BITMASK_W_MASK)) != 0; +} + +/* Sets the bit at (x,y) */ +static INLINE void bitmask_setbit(bitmask_t *m, int x, int y) +{ + m->bits[x/BITMASK_W_LEN*m->h + y] |= BITMASK_N(x & BITMASK_W_MASK); +} + +/* Clears the bit at (x,y) */ +static INLINE void bitmask_clearbit(bitmask_t *m, int x, int y) +{ + m->bits[x/BITMASK_W_LEN*m->h + y] &= ~BITMASK_N(x & BITMASK_W_MASK); +} + +/* Returns nonzero if the masks overlap with the given offset. + The overlap tests uses the following offsets (which may be negative): + + +----+----------.. + |A | yoffset + | +-+----------.. + +--|B + |xoffset + | | + : : +*/ +int bitmask_overlap(const bitmask_t *a, const bitmask_t *b, int xoffset, int yoffset); + +/* Like bitmask_overlap(), but will also give a point of intersection. + x and y are given in the coordinates of mask a, and are untouched + if there is no overlap. */ +int bitmask_overlap_pos(const bitmask_t *a, const bitmask_t *b, + int xoffset, int yoffset, int *x, int *y); + +/* Returns the number of overlapping 'pixels' */ +int bitmask_overlap_area(const bitmask_t *a, const bitmask_t *b, int xoffset, int yoffset); + +/* Fills a mask with the overlap of two other masks. A bitwise AND. */ +void bitmask_overlap_mask (const bitmask_t *a, const bitmask_t *b, bitmask_t *c, int xoffset, int yoffset); + +/* Draws mask b onto mask a (bitwise OR). Can be used to compose large + (game background?) mask from several submasks, which may speed up + the testing. */ + +void bitmask_draw(bitmask_t *a, const bitmask_t *b, int xoffset, int yoffset); + +void bitmask_erase(bitmask_t *a, const bitmask_t *b, int xoffset, int yoffset); + +/* Return a new scaled bitmask, with dimensions w*h. The quality of the + scaling may not be perfect for all circumstances, but it should + be reasonable. If either w or h is 0 a clear 1x1 mask is returned. */ +bitmask_t *bitmask_scale(const bitmask_t *m, int w, int h); + +/* Convolve b into a, drawing the output into o, shifted by offset. If offset + * is 0, then the (x,y) bit will be set if and only if + * bitmask_overlap(a, b, x - b->w - 1, y - b->h - 1) returns true. + * + * Modifies bits o[xoffset ... xoffset + a->w + b->w - 1) + * [yoffset ... yoffset + a->h + b->h - 1). */ +void bitmask_convolve(const bitmask_t *a, const bitmask_t *b, bitmask_t *o, int xoffset, int yoffset); + +/* Find the closest point to (x,y) with mask[x,y] not set */ +void bitmask_find_closest(const bitmask_t *mask, int * x, int * y); + +#ifdef __cplusplus +} /* End of extern "C" { */ +#endif + +#endif Added: pygame/src/mask.c =================================================================== --- pygame/src/mask.c (rev 0) +++ pygame/src/mask.c 2008-12-03 00:46:08 UTC (rev 105) @@ -0,0 +1,1432 @@ +/* + Copyright (C) 2002-2007 Ulf Ekstrom except for the bitcount function. + This wrapper code was originally written by Danny van Bruggen(?) for + the SCAM library, it was then converted by Ulf Ekstrom to wrap the + bitmask library, a spinoff from SCAM. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + +/* a couple of print debugging helpers */ +/* +#define CALLLOG2(x,y) fprintf(stderr, (x), (y)); +#define CALLLOG(x) fprintf(stderr, (x)); +*/ + +#include "pygame.h" +#include "pygamedocs.h" +#include "structmember.h" +#include "bitmask.h" +#include <math.h> + +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif + +typedef struct { + PyObject_HEAD + bitmask_t *mask; +} PyMaskObject; + +staticforward PyTypeObject PyMask_Type; +#define PyMask_Check(x) ((x)->ob_type == &PyMask_Type) +#define PyMask_AsBitmap(x) (((PyMaskObject*)x)->mask) + + +/* mask object methods */ + +static PyObject* mask_get_size(PyObject* self, PyObject* args) +{ + bitmask_t *mask = PyMask_AsBitmap(self); + + if(!PyArg_ParseTuple(args, "")) + return NULL; + + return Py_BuildValue("(ii)", mask->w, mask->h); +} + +static PyObject* mask_get_at(PyObject* self, PyObject* args) +{ + bitmask_t *mask = PyMask_AsBitmap(self); + int x, y, val; + + if(!PyArg_ParseTuple(args, "(ii)", &x, &y)) + return NULL; + if (x >= 0 && x < mask->w && y >= 0 && y < mask->h) { + val = bitmask_getbit(mask, x, y); + } else { + PyErr_Format(PyExc_IndexError, "%d, %d is out of bounds", x, y); + return NULL; + } + + return PyInt_FromLong(val); +} + +static PyObject* mask_set_at(PyObject* self, PyObject* args) +{ + bitmask_t *mask = PyMask_AsBitmap(self); + int x, y, value = 1; + + if(!PyArg_ParseTuple(args, "(ii)|i", &x, &y, &value)) + return NULL; + if (x >= 0 && x < mask->w && y >= 0 && y < mask->h) { + if (value) { + bitmask_setbit(mask, x, y); + } else { + bitmask_clearbit(mask, x, y); + } + } else { + PyErr_Format(PyExc_IndexError, "%d, %d is out of bounds", x, y); + return NULL; + } + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject* mask_overlap(PyObject* self, PyObject* args) +{ + bitmask_t *mask = PyMask_AsBitmap(self); + bitmask_t *othermask; + PyObject *maskobj; + int x, y, val; + int xp,yp; + + if(!PyArg_ParseTuple(args, "O!(ii)", &PyMask_Type, &maskobj, &x, &y)) + return NULL; + othermask = PyMask_AsBitmap(maskobj); + + val = bitmask_overlap_pos(mask, othermask, x, y, &xp, &yp); + if (val) { + return Py_BuildValue("(ii)", xp,yp); + } else { + Py_INCREF(Py_None); + return Py_None; + } +} + + +static PyObject* mask_overlap_area(PyObject* self, PyObject* args) +{ + bitmask_t *mask = PyMask_AsBitmap(self); + bitmask_t *othermask; + PyObject *maskobj; + int x, y, val; + + if(!PyArg_ParseTuple(args, "O!(ii)", &PyMask_Type, &maskobj, &x, &y)) { + return NULL; + } + othermask = PyMask_AsBitmap(maskobj); + + val = bitmask_overlap_area(mask, othermask, x, y); + return PyInt_FromLong(val); +} + +static PyObject* mask_overlap_mask(PyObject* self, PyObject* args) +{ + int x, y; + bitmask_t *mask = PyMask_AsBitmap(self); + bitmask_t *output = bitmask_create(mask->w, mask->h); + bitmask_t *othermask; + PyObject *maskobj; + PyMaskObject *maskobj2 = PyObject_New(PyMaskObject, &PyMask_Type); + + if(!PyArg_ParseTuple(args, "O!(ii)", &PyMask_Type, &maskobj, &x, &y)) { + return NULL; + } + othermask = PyMask_AsBitmap(maskobj); + + bitmask_overlap_mask(mask, othermask, output, x, y); + + if(maskobj2) + maskobj2->mask = output; + + return (PyObject*)maskobj2; +} + +static PyObject* mask_fill(PyObject* self, PyObject* args) +{ + bitmask_t *mask = PyMask_AsBitmap(self); + + bitmask_fill(mask); + + Py_RETURN_NONE; +} + +static PyObject* mask_clear(PyObject* self, PyObject* args) +{ + bitmask_t *mask = PyMask_AsBitmap(self); + + bitmask_clear(mask); + + Py_RETURN_NONE; +} + +static PyObject* mask_invert(PyObject* self, PyObject* args) +{ + bitmask_t *mask = PyMask_AsBitmap(self); + + bitmask_invert(mask); + + Py_RETURN_NONE; +} + +static PyObject* mask_scale(PyObject* self, PyObject* args) +{ + int x, y; + bitmask_t *input = PyMask_AsBitmap(self); + bitmask_t *output; + PyMaskObject *maskobj = PyObject_New(PyMaskObject, &PyMask_Type); + + if(!PyArg_ParseTuple(args, "(ii)", &x, &y)) { + return NULL; + } + + output = bitmask_scale(input, x, y); + + if(maskobj) + maskobj->mask = output; + + return (PyObject*)maskobj; +} + +static PyObject* mask_draw(PyObject* self, PyObject* args) +{ + bitmask_t *mask = PyMask_AsBitmap(self); + bitmask_t *othermask; + PyObject *maskobj; + int x, y; + + if(!PyArg_ParseTuple(args, "O!(ii)", &PyMask_Type, &maskobj, &x, &y)) { + return NULL; + } + othermask = PyMask_AsBitmap(maskobj); + + bitmask_draw(mask, othermask, x, y); + + Py_RETURN_NONE; +} + +static PyObject* mask_erase(PyObject* self, PyObject* args) +{ + bitmask_t *mask = PyMask_AsBitmap(self); + bitmask_t *othermask; + PyObject *maskobj; + int x, y; + + if(!PyArg_ParseTuple(args, "O!(ii)", &PyMask_Type, &maskobj, &x, &y)) { + return NULL; + } + othermask = PyMask_AsBitmap(maskobj); + + bitmask_erase(mask, othermask, x, y); + + Py_RETURN_NONE; +} + +static PyObject* mask_count(PyObject* self, PyObject* args) +{ + bitmask_t *m = PyMask_AsBitmap(self); + + return PyInt_FromLong(bitmask_count(m)); +} + +static PyObject* mask_centroid(PyObject* self, PyObject* args) +{ + bitmask_t *mask = PyMask_AsBitmap(self); + int x, y; + long int m10, m01, m00; + PyObject *xobj, *yobj; + + m10 = m01 = m00 = 0; + + for (x = 0; x < mask->w; x++) { + for (y = 0; y < mask->h; y++) { + if (bitmask_getbit(mask, x, y)) { + m10 += x; + m01 += y; + m00++; + } + } + } + + if (m00) { + xobj = PyInt_FromLong(m10/m00); + yobj = PyInt_FromLong(m01/m00); + } else { + xobj = PyInt_FromLong(0); + yobj = PyInt_FromLong(0); + } + + return Py_BuildValue("(NN)", xobj, yobj); +} + +static PyObject* mask_angle(PyObject* self, PyObject* args) +{ + bitmask_t *mask = PyMask_AsBitmap(self); + int x, y, xc, yc; + long int m10, m01, m00, m20, m02, m11; + double theta; + + m10 = m01 = m00 = m20 = m02 = m11 = 0; + + for (x = 0; x < mask->w; x++) { + for (y = 0; y < mask->h; y++) { + if (bitmask_getbit(mask, x, y)) { + m10 += x; + m20 += x*x; + m11 += x*y; + m02 += y*y; + m01 += y; + m00++; + } + } + } + + if (m00) { + xc = m10/m00; + yc = m01/m00; + theta = -90.0*atan2(2*(m11/m00 - xc*yc),(m20/m00 - xc*xc)-(m02/m00 - yc*yc))/M_PI; + return PyFloat_FromDouble(theta); + } else { + return PyFloat_FromDouble(0); + } +} + +static PyObject* mask_outline(PyObject* self, PyObject* args) +{ + bitmask_t* c = PyMask_AsBitmap(self); + bitmask_t* m = bitmask_create(c->w + 2, c->h + 2); + PyObject *plist, *value; + int x, y, every, e, firstx, firsty, secx, secy, currx, curry, nextx, nexty, n; + int a[14], b[14]; + a[0] = a[1] = a[7] = a[8] = a[9] = b[1] = b[2] = b[3] = b[9] = b[10] = b[11]= 1; + a[2] = a[6] = a[10] = b[4] = b[0] = b[12] = b[8] = 0; + a[3] = a[4] = a[5] = a[11] = a[12] = a[13] = b[5] = b[6] = b[7] = b[13] = -1; + + plist = NULL; + plist = PyList_New (0); + if (!plist) + return NULL; + + every = 1; + n = firstx = firsty = secx = x = 0; + + if(!PyArg_ParseTuple(args, "|i", &every)) { + return NULL; + } + + /* by copying to a new, larger mask, we avoid having to check if we are at + a border pixel every time. */ + bitmask_draw(m, c, 1, 1); + + e = every; + + /* find the first set pixel in the mask */ + for (y = 1; y < m->h-1; y++) { + for (x = 1; x < m->w-1; x++) { + if (bitmask_getbit(m, x, y)) { + firstx = x; + firsty = y; + value = Py_BuildValue("(ii)", x-1, y-1); + PyList_Append(plist, value); + Py_DECREF(value); + break; + } + } + if (bitmask_getbit(m, x, y)) + break; + } + + + + /* covers the mask having zero pixels or only the final pixel */ + if ((x == m->w-1) && (y == m->h-1)) { + bitmask_free(m); + return plist; + } + + /* check just the first pixel for neighbors */ + for (n = 0;n < 8;n++) { + if (bitmask_getbit(m, x+a[n], y+b[n])) { + currx = secx = x+a[n]; + curry = secy = y+b[n]; + e--; + if (!e) { + e = every; + value = Py_BuildValue("(ii)", secx-1, secy-1); + PyList_Append(plist, value); + Py_DECREF(value); + } + break; + } + } + + /* if there are no neighbors, return */ + if (!secx) { + bitmask_free(m); + return plist; + } + + /* the outline tracing loop */ + for (;;) { + /* look around the pixel, it has to have a neighbor */ + for (n = (n + 6) & 7;;n++) { + if (bitmask_getbit(m, currx+a[n], curry+b[n])) { + nextx = currx+a[n]; + nexty = curry+b[n]; + e--; + if (!e) { + e = every; + if ((curry == firsty && currx == firstx) && (secx == nextx && secy == nexty)) { + break; + } + value = Py_BuildValue("(ii)", nextx-1, nexty-1); + PyList_Append(plist, value); + Py_DECREF(value); + } + break; + } + } + /* if we are back at the first pixel, and the next one will be the + second one we visited, we are done */ + if ((curry == firsty && currx == firstx) && (secx == nextx && secy == nexty)) { + break; + } + + curry = nexty; + currx = nextx; + } + + bitmask_free(m); + + return plist; +} + +static PyObject* mask_convolve(PyObject* aobj, PyObject* args) +{ + PyObject *bobj, *oobj = Py_None; + bitmask_t *a, *b, *o; + int xoffset = 0, yoffset = 0; + + if (!PyArg_ParseTuple (args, "O!|O(ii)", &PyMask_Type, &bobj, &oobj, &xoffset, &yoffset)) + return NULL; + + a = PyMask_AsBitmap(aobj); + b = PyMask_AsBitmap(bobj); + + if (oobj == Py_None) { + PyMaskObject *result = PyObject_New(PyMaskObject, &PyMask_Type); + + result->mask = bitmask_create(a->w + b->w - 1, a->h + b->h - 1); + oobj = (PyObject*) result; + } + else + Py_INCREF(oobj); + + o = PyMask_AsBitmap(oobj); + + bitmask_convolve(a, b, o, xoffset, yoffset); + return oobj; +} + +static PyObject * mask_find_closest(PyObject* self, PyObject* args) +{ + bitmask_t * mask = PyMask_AsBitmap(self); + int x, y; + + if (!PyArg_ParseTuple(args, "(ii)", &x, &y)) + return NULL; + + bitmask_find_closest(mask, &x, &y); + + return Py_BuildValue("(ii)", x, y); +} + +static PyObject* mask_from_surface(PyObject* self, PyObject* args) +{ + bitmask_t *mask; + SDL_Surface* surf; + + PyObject* surfobj; + PyMaskObject *maskobj; + + int x, y, threshold, ashift, aloss, usethresh; + Uint8 *pixels; + + SDL_PixelFormat *format; + Uint32 color, amask; + Uint8 *pix; + Uint8 a; + + /* set threshold as 127 default argument. */ + threshold = 127; + + /* get the surface from the passed in arguments. + * surface, threshold + */ + + if (!PyArg_ParseTuple (args, "O!|i", &PySurface_Type, &surfobj, &threshold)) { + return NULL; + } + + surf = PySurface_AsSurface(surfobj); + + /* lock the surface, release the GIL. */ + PySurface_Lock (surfobj); + + Py_BEGIN_ALLOW_THREADS; + + /* get the size from the surface, and create the mask. */ + mask = bitmask_create(surf->w, surf->h); + + + if(!mask) { + /* Py_END_ALLOW_THREADS; + */ + return NULL; /*RAISE(PyExc_Error, "cannot create bitmask");*/ + } + + pixels = (Uint8 *) surf->pixels; + format = surf->format; + amask = format->Amask; + ashift = format->Ashift; + aloss = format->Aloss; + usethresh = !(surf->flags & SDL_SRCCOLORKEY); + + for(y=0; y < surf->h; y++) { + pixels = (Uint8 *) surf->pixels + y*surf->pitch; + for(x=0; x < surf->w; x++) { + /* Get the color. TODO: should use an inline helper + * function for this common function. */ + switch (format->BytesPerPixel) + { + case 1: + color = (Uint32)*((Uint8 *) pixels); + pixels++; + break; + case 2: + color = (Uint32)*((Uint16 *) pixels); + pixels += 2; + break; + case 3: + pix = ((Uint8 *) pixels); + pixels += 3; + #if SDL_BYTEORDER == SDL_LIL_ENDIAN + color = (pix[0]) + (pix[1] << 8) + (pix[2] << 16); + #else + color = (pix[2]) + (pix[1] << 8) + (pix[0] << 16); + #endif + break; + default: /* case 4: */ + color = *((Uint32 *) pixels); + pixels += 4; + break; + } + + + if (usethresh) { + a = ((color & amask) >> ashift) << aloss; + /* no colorkey, so we check the threshold of the alpha */ + if (a > threshold) { + bitmask_setbit(mask, x, y); + } + } else { + /* test against the colour key. */ + if (format->colorkey != color) { + bitmask_setbit(mask, x, y); + } + } + } + } + + Py_END_ALLOW_THREADS; + + /* unlock the surface, release the GIL. + */ + PySurface_Unlock (surfobj); + + /*create the new python object from mask*/ + maskobj = PyObject_New(PyMaskObject, &PyMask_Type); + if(maskobj) + maskobj->mask = mask; + + + return (PyObject*)maskobj; +} + +void bitmask_threshold (bitmask_t *m, SDL_Surface *surf, SDL_Surface *surf2, + Uint32 color, Uint32 threshold) +{ + int x, y, rshift, gshift, bshift, rshift2, gshift2, bshift2; + int rloss, gloss, bloss, rloss2, gloss2, bloss2; + Uint8 *pixels, *pixels2; + SDL_PixelFormat *format, *format2; + Uint32 the_color, the_color2, rmask, gmask, bmask, rmask2, gmask2, bmask2; + Uint8 *pix; + Uint8 r, g, b, a; + Uint8 tr, tg, tb, ta; + + pixels = (Uint8 *) surf->pixels; + format = surf->format; + rmask = format->Rmask; + gmask = format->Gmask; + bmask = format->Bmask; + rshift = format->Rshift; + gshift = format->Gshift; + bshift = format->Bshift; + rloss = format->Rloss; + gloss = format->Gloss; + bloss = format->Bloss; + + if(surf2) { + format2 = surf2->format; + rmask2 = format2->Rmask; + gmask2 = format2->Gmask; + bmask2 = format2->Bmask; + rshift2 = format2->Rshift; + gshift2 = format2->Gshift; + bshift2 = format2->Bshift; + rloss2 = format2->Rloss; + gloss2 = format2->Gloss; + bloss2 = format2->Bloss; + pixels2 = (Uint8 *) surf2->pixels; + } else { /* make gcc stop complaining */ + rmask2 = gmask2 = bmask2 = 0; + rshift2 = gshift2 = bshift2 = 0; + rloss2 = gloss2 = bloss2 = 0; + format2 = NULL; + pixels2 = NULL; + } + + SDL_GetRGBA (color, format, &r, &g, &b, &a); + SDL_GetRGBA (threshold, format, &tr, &tg, &tb, &ta); + + for(y=0; y < surf->h; y++) { + pixels = (Uint8 *) surf->pixels + y*surf->pitch; + if (surf2) { + pixels2 = (Uint8 *) surf2->pixels + y*surf2->pitch; + } + for(x=0; x < surf->w; x++) { + /* the_color = surf->get_at(x,y) */ + switch (format->BytesPerPixel) + { + case 1: + the_color = (Uint32)*((Uint8 *) pixels); + pixels++; + break; + case 2: + the_color = (Uint32)*((Uint16 *) pixels); + pixels += 2; + break; + case 3: + pix = ((Uint8 *) pixels); + pixels += 3; +#if SDL_BYTEORDER == SDL_LIL_ENDIAN + the_color = (pix[0]) + (pix[1] << 8) + (pix[2] << 16); +#else + the_color = (pix[2]) + (pix[1] << 8) + (pix[0] << 16); +#endif + break; + default: /* case 4: */ + the_color = *((Uint32 *) pixels); + pixels += 4; + break; + } + + if (surf2) { + switch (format2->BytesPerPixel) { + case 1: + the_color2 = (Uint32)*((Uint8 *) pixels2); + pixels2++; + break; + case 2: + the_color2 = (Uint32)*((Uint16 *) pixels2); + pixels2 += 2; + break; + case 3: + pix = ((Uint8 *) pixels2); + pixels2 += 3; +#if SDL_BYTEORDER == SDL_LIL_ENDIAN + the_color2 = (pix[0]) + (pix[1] << 8) + (pix[2] << 16); +#else + the_color2 = (pix[2]) + (pix[1] << 8) + (pix[0] << 16); +#endif + break; + default: /* case 4: */ + the_color2 = *((Uint32 *) pixels2); + pixels2 += 4; + break; + } + if ((abs((((the_color2 & rmask2) >> rshift2) << rloss2) - (((the_color & rmask) >> rshift) << rloss)) < tr) & + (abs((((the_color2 & gmask2) >> gshift2) << gloss2) - (((the_color & gmask) >> gshift) << gloss)) < tg) & + (abs((((the_color2 & bmask2) >> bshift2) << bloss2) - (((the_color & bmask) >> bshift) << bloss)) < tb)) { + /* this pixel is within the threshold of othersurface. */ + bitmask_setbit(m, x, y); + } + } else if ((abs((((the_color & rmask) >> rshift) << rloss) - r) < tr) & + (abs((((the_color & gmask) >> gshift) << gloss) - g) < tg) & + (abs((((the_color & bmask) >> bshift) << bloss) - b) < tb)) { + /* this pixel is within the threshold of the color. */ + bitmask_setbit(m, x, y); + } + } + } +} + +static PyObject* mask_from_threshold(PyObject* self, PyObject* args) +{ + PyObject *surfobj, *surfobj2; + PyMaskObject *maskobj; + bitmask_t* m; + SDL_Surface* surf, *surf2; + int bpp; + PyObject *rgba_obj_color, *rgba_obj_threshold; + Uint8 rgba_color[4]; + Uint8 rgba_threshold[4]; + Uint32 color; + Uint32 color_threshold; + + surf2 = surf = NULL; + surfobj2 = NULL; + rgba_obj_threshold = NULL; + rgba_threshold[0] = 0; rgba_threshold[1] = 0; rgba_threshold[2] = 0; rgba_threshold[4] = 255; + + if (!PyArg_ParseTuple (args, "O!O|OO!", &PySurface_Type, &surfobj, + &rgba_obj_color, &rgba_obj_threshold, + &PySurface_Type, &surfobj2)) + return NULL; + + surf = PySurface_AsSurface (surfobj); + if(surfobj2) { + surf2 = PySurface_AsSurface (surfobj2); + } + + if (PyInt_Check (rgba_obj_color)) { + color = (Uint32) PyInt_AsLong (rgba_obj_color); + } else if (PyLong_Check (rgba_obj_color)) { + color = (Uint32) PyLong_AsUnsignedLong (rgba_obj_color); + } else if (RGBAFromColorObj (rgba_obj_color, rgba_color)) { + color = SDL_MapRGBA (surf->format, rgba_color[0], rgba_color[1], + rgba_color[2], rgba_color[3]); + } else { + return RAISE (PyExc_TypeError, "invalid color argument"); + } + + if(rgba_obj_threshold) { + if (PyInt_Check (rgba_obj_threshold)) + color_threshold = (Uint32) PyInt_AsLong (rgba_obj_threshold); + else if (PyLong_Check (rgba_obj_threshold)) + color_threshold = (Uint32) PyLong_AsUnsignedLong + (rgba_obj_threshold); + else if (RGBAFromColorObj (rgba_obj_threshold, rgba_threshold)) + color_threshold = SDL_MapRGBA (surf->format, rgba_threshold[0], rgba_threshold[1], rgba_threshold[2], rgba_threshold[3]); + else + return RAISE (PyExc_TypeError, "invalid threshold argument"); + } else { + color_threshold = SDL_MapRGBA (surf->format, rgba_threshold[0], rgba_threshold[1], rgba_threshold[2], rgba_threshold[3]); + } + + bpp = surf->format->BytesPerPixel; + m = bitmask_create(surf->w, surf->h); + + PySurface_Lock(surfobj); + if(surfobj2) { + PySurface_Lock(surfobj2); + } + + Py_BEGIN_ALLOW_THREADS; + bitmask_threshold (m, surf, surf2, color, color_threshold); + Py_END_ALLOW_THREADS; + + PySurface_Unlock(surfobj); + if(surfobj2) { + PySurface_Unlock(surfobj2); + } + + maskobj = PyObject_New(PyMaskObject, &PyMask_Type); + if(maskobj) + maskobj->mask = m; + + return (PyObject*)maskobj; +} + + + + +/* the initial labelling phase of the connected components algorithm + +Returns: The highest label in the labelled image + +input - The input Mask +image - An array to store labelled pixels +ufind - The union-find label equivalence array +largest - An array to store the number of pixels for each label + +*/ +unsigned int cc_label(bitmask_t *input, unsigned int* image, unsigned int* ufind, unsigned int* largest) { + unsigned int *buf; + unsigned int x, y, w, h, root, aroot, croot, temp, label; + + label = 0; + w = input->w; + h = input->h; + + ufind[0] = 0; + buf = image; + + /* special case for first pixel */ + if(bitmask_getbit(input, 0, 0)) { /* process for a new connected comp: */ + label++; /* create a new label */ + *buf = label; /* give the pixel the label */ + ufind[label] = label; /* put the label in the equivalence array */ + largest[label] = 1; /* the label has 1 pixel associated with it */ + } else { + *buf = 0; + } + buf++; + + + + /* special case for first row. + Go over the first row except the first pixel. + */ + for(x = 1; x < w; x++) { + if (bitmask_getbit(input, x, 0)) { + if (*(buf-1)) { /* d label */ + *buf = *(buf-1); + } else { /* create label */ + label++; + *buf = label; + ufind[label] = label; + largest[label] = 0; + } + largest[*buf]++; + } else { + *buf = 0; + } + buf++; + } + + + + /* the rest of the image */ + for(y = 1; y < h; y++) { + /* first pixel of the row */ + if (bitmask_getbit(input, 0, y)) { + if (*(buf-w)) { /* b label */ + *buf = *(buf-w); + } else if (*(buf-w+1)) { /* c label */ + *buf = *(buf-w+1); + } else { /* create label */ + label++; + *buf = label; + ufind[label] = label; + largest[label] = 0; + } + largest[*buf]++; + } else { + *buf = 0; + } + buf++; + /* middle pixels of the row */ + for(x = 1; x < (w-1); x++) { + if (bitmask_getbit(input, x, y)) { + if (*(buf-w)) { /* b label */ + *buf = *(buf-w); + } else if (*(buf-w+1)) { /* c branch of tree */ + if (*(buf-w-1)) { /* union(c, a) */ + croot = root = *(buf-w+1); + while (ufind[root] < root) { /* find root */ + root = ufind[root]; + } + if (croot != *(buf-w-1)) { + temp = aroot = *(buf-w-1); + while (ufind[aroot] < aroot) { /* find root */ + aroot = ufind[aroot]; + ... [truncated message content] |
From: <mdg...@us...> - 2008-12-03 00:45:12
|
Revision: 104 http://pen.svn.sourceforge.net/pen/?rev=104&view=rev Author: mdgeorge Date: 2008-12-03 00:45:07 +0000 (Wed, 03 Dec 2008) Log Message: ----------- Replacing symlinks. Removed Paths: ------------- pygame/src/bitmask.c pygame/src/bitmask.h pygame/src/mask.c pygame/src/mask.doc pygame/src/pygamedocs.h pygame/test/mask_test.py Deleted: pygame/src/bitmask.c =================================================================== --- pygame/src/bitmask.c 2008-12-01 18:26:50 UTC (rev 103) +++ pygame/src/bitmask.c 2008-12-03 00:45:07 UTC (rev 104) @@ -1 +0,0 @@ -link /home/mike/src/pygame/src/bitmask.c \ No newline at end of file Deleted: pygame/src/bitmask.h =================================================================== --- pygame/src/bitmask.h 2008-12-01 18:26:50 UTC (rev 103) +++ pygame/src/bitmask.h 2008-12-03 00:45:07 UTC (rev 104) @@ -1 +0,0 @@ -link /home/mike/src/pygame/src/bitmask.h \ No newline at end of file Deleted: pygame/src/mask.c =================================================================== --- pygame/src/mask.c 2008-12-01 18:26:50 UTC (rev 103) +++ pygame/src/mask.c 2008-12-03 00:45:07 UTC (rev 104) @@ -1 +0,0 @@ -link /home/mike/src/pygame/src/mask.c \ No newline at end of file Deleted: pygame/src/mask.doc =================================================================== --- pygame/src/mask.doc 2008-12-01 18:26:50 UTC (rev 103) +++ pygame/src/mask.doc 2008-12-03 00:45:07 UTC (rev 104) @@ -1 +0,0 @@ -link /home/mike/src/pygame/src/mask.doc \ No newline at end of file Deleted: pygame/src/pygamedocs.h =================================================================== --- pygame/src/pygamedocs.h 2008-12-01 18:26:50 UTC (rev 103) +++ pygame/src/pygamedocs.h 2008-12-03 00:45:07 UTC (rev 104) @@ -1 +0,0 @@ -link /home/mike/src/pygame/src/pygamedocs.h \ No newline at end of file Deleted: pygame/test/mask_test.py =================================================================== --- pygame/test/mask_test.py 2008-12-01 18:26:50 UTC (rev 103) +++ pygame/test/mask_test.py 2008-12-03 00:45:07 UTC (rev 104) @@ -1 +0,0 @@ -link /home/mike/src/pygame/test/mask_test.py \ No newline at end of file This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <js...@us...> - 2008-12-01 18:26:53
|
Revision: 103 http://pen.svn.sourceforge.net/pen/?rev=103&view=rev Author: jstrout Date: 2008-12-01 18:26:50 +0000 (Mon, 01 Dec 2008) Log Message: ----------- Added a Lever part type, which is a sort of beam fixed to the wall at the center, but free to rotate around that point. This required refactoring the physics engine a little, and touching all the other part files. Modified Paths: -------------- data/parts/anvil.py data/parts/balloon.py data/parts/balls.py data/parts/blocks.py data/parts/shelf.py src/globals.py src/level.py src/parts.py src/physics.py Added Paths: ----------- data/images/lever.png data/parts/lever.py Added: data/images/lever.png =================================================================== (Binary files differ) Property changes on: data/images/lever.png ___________________________________________________________________ Added: svn:mime-type + application/octet-stream Modified: data/parts/anvil.py =================================================================== --- data/parts/anvil.py 2008-11-30 21:46:54 UTC (rev 102) +++ data/parts/anvil.py 2008-12-01 18:26:50 UTC (rev 103) @@ -6,7 +6,7 @@ Part.__init__( self, type, *args, **kwargs ) def get_model( self ): - "return (body, shape) tuple that is the physics model of this part" + "return (shape, body) tuple that is the physics model of this part" # br tr tl bl verts = [(36, 23), (21, -16), (-19, -16), (-37, 23)] mass = 1000 @@ -18,7 +18,7 @@ shape.elasticity = 0.1 shape.friction = 0.5 - return body, shape + return shape, body create_type( "Anvil", description="Glorified paperweight", image=load_image( "anvil.png",-1 ), create_instance=Anvil ) Modified: data/parts/balloon.py =================================================================== --- data/parts/balloon.py 2008-11-30 21:46:54 UTC (rev 102) +++ data/parts/balloon.py 2008-12-01 18:26:50 UTC (rev 103) @@ -6,7 +6,7 @@ Part.__init__( self, type, *args, **kwargs ) def get_model( self ): - "return (body, shape) tuple that is the physics model of this part" + "return (shape, body) tuple that is the physics model of this part" radius = 25 mass = 1 moment = pymunk.moment_for_circle(mass, 0, radius, (0,0)) @@ -16,7 +16,7 @@ shape.elasticity = 0.8 shape.friction = 0.4 - return body, shape + return shape, body create_type( "Balloon", description="Filled with helium", image=load_image( "balloon.png",-1 ), create_instance=Balloon ) Modified: data/parts/balls.py =================================================================== --- data/parts/balls.py 2008-11-30 21:46:54 UTC (rev 102) +++ data/parts/balls.py 2008-12-01 18:26:50 UTC (rev 103) @@ -9,7 +9,7 @@ Part.__init__( self, type, *args, **kwargs ) def get_model( self ): - "return (body, shape) tuple that is the physics model of this part" + "return (shape, body) tuple that is the physics model of this part" radius = self.type.radius mass = self.type.mass moment = pymunk.moment_for_circle(mass, 0, radius, (0,0)) @@ -19,7 +19,7 @@ shape.elasticity = self.type.elasticity shape.friction = 0.8 - return body, shape + return shape, body def create_ball_type( name, descr, image, density=None, radius=None, bounce=0.5 ): Modified: data/parts/blocks.py =================================================================== --- data/parts/blocks.py 2008-11-30 21:46:54 UTC (rev 102) +++ data/parts/blocks.py 2008-12-01 18:26:50 UTC (rev 103) @@ -8,7 +8,7 @@ class Block( parts.RotatingPart ): def get_model( self ): - "return (body, shape) tuple that is the physics model of this part" + "return (shape, body) tuple that is the physics model of this part" width, height, depth = self.type.width, self.type.height, self.type.depth hw = width/2 hh = height/2 @@ -22,7 +22,7 @@ shape.friction = 0.8 body.angle = - math.radians(self.angle) - return body, shape + return shape, body def create_block_type( name, descr, image, density=None, size=None, bounce=0.3 ): try: Added: data/parts/lever.py =================================================================== --- data/parts/lever.py (rev 0) +++ data/parts/lever.py 2008-12-01 18:26:50 UTC (rev 103) @@ -0,0 +1,59 @@ +from __future__ import division +import pygame +import parts +import math +import pymunk +from globals import load_image + +class Lever( parts.RotatingPart ): + + def get_model( self ): + "return (shape, body) tuple that is the physics model of this part" + # TODO: construct a more complex shape that's a better fit + # for the sprite here. (Note that the poly must be convex, + # and wound CCW.) + points = [(1,7), (4,4), (37,2), (40,2), (74,4), (76,7), + (74,10), (41,11), (36,11), (3,10)] + offset = (-38.5, -6.5) # negative of center coordinate + points.reverse() # get them in CCW order + + mass = self.type.mass + moment = pymunk.moment_for_poly(mass, points, offset) + body = pymunk.Body(mass, moment) + + shape = pymunk.Poly(body, points, offset) + shape.elasticity = 0.3 + shape.friction = 0.8 + + body.angle = - math.radians(self.angle) + body.position = self.pos + + # Above is all the standard stuff. But now we need to create + # a static body for the pivot point. Because this is static, + # we don't need to return it (to have it added to the space). + pivot_body = pymunk.Body(pymunk.inf, pymunk.inf) + pivot_body.position = self.pos + + # Finally, we need to create a PinJoint linking the the + # lever to that pivot, and the PinJoint needs to be added to + # the space. + joint = pymunk.PinJoint(body, pivot_body, (0,0), (0,0)) + + return shape, body, joint + +def create_lever_type(): + image = load_image( "lever.png", -1 ) + type = parts.create_type( "Lever", + image=image, + description = "Rotates around a center mount", + create_instance = Lever) + + density = 5.0 + width = image.get_width() + height = image.get_height() + depth = height + volume = width * height * depth + type.mass = density * volume + + +create_lever_type() \ No newline at end of file Modified: data/parts/shelf.py =================================================================== --- data/parts/shelf.py 2008-11-30 21:46:54 UTC (rev 102) +++ data/parts/shelf.py 2008-12-01 18:26:50 UTC (rev 103) @@ -8,7 +8,7 @@ class Shelf( parts.RotatingPart ): def get_model( self ): - "return (body, shape) tuple that is the physics model of this part" + "return (shape, body) tuple that is the physics model of this part" width, height, depth = self.type.width, self.type.height, self.type.depth hw = width/2 hh = height/2 @@ -22,7 +22,8 @@ shape.friction = 0.8 body.angle = - math.radians(self.angle) - return body, shape + body.position = self.pos + return shape def create_shelf_type( name, descr, image, density=None, size=None, bounce=0.5 ): try: Modified: src/globals.py =================================================================== --- src/globals.py 2008-11-30 21:46:54 UTC (rev 102) +++ src/globals.py 2008-12-01 18:26:50 UTC (rev 103) @@ -12,10 +12,9 @@ print 'Cannot load image: ', name raise SystemExit, message image = image.convert() - if colorkey is not None: - if colorkey is -1: - colorkey = image.get_at((0,0)) - image.set_colorkey(colorkey, pygame.RLEACCEL) + if colorkey is -1: + colorkey = image.get_at((0,0)) + image.set_colorkey(colorkey, pygame.RLEACCEL) return image def load_font( name, size, bold = False ): Modified: src/level.py =================================================================== --- src/level.py 2008-11-30 21:46:54 UTC (rev 102) +++ src/level.py 2008-12-01 18:26:50 UTC (rev 103) @@ -41,7 +41,7 @@ def get_part( self, type, **params ): """Removes an available part and returns a placed part""" - if self._avail[type] < 1: + if self._avail.get(type, 0) < 1: return None else: self._avail[type] -= 1 Modified: src/parts.py =================================================================== --- src/parts.py 2008-11-30 21:46:54 UTC (rev 102) +++ src/parts.py 2008-12-01 18:26:50 UTC (rev 103) @@ -38,7 +38,8 @@ """Return the physics model for this part. If you return None, then it will be a display-only part that does not participate in the physics simulation at all. - For the Pymunk engine, this must be a (body, shape) tuple.""" + For the Pymunk engine, the shape must come first, and the body (if any) + should come second.""" return None def get_sim_parts( self ): Modified: src/physics.py =================================================================== --- src/physics.py 2008-11-30 21:46:54 UTC (rev 102) +++ src/physics.py 2008-12-01 18:26:50 UTC (rev 103) @@ -103,12 +103,13 @@ # Add the body and shape to the physics engine. part_model = part.get_model() if part_model is None: return - body, shape = part_model - body.position = part.pos - if body.mass >= pymunk.inf: - self.space.add_static(shape) - else: - self.space.add(body, shape) + shape = None + if not hasattr(part_model, '__iter__'): part_model = [part_model] + shape = part_model[0] + if len(part_model) > 1: + body = part_model[1] + body.position = part.pos + self.space.add( *part_model ) # Store the physics model (i.e. shape) on the sprite, so we can # update it later; and keep a list of them for the same reason This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <js...@us...> - 2008-11-30 21:46:56
|
Revision: 102 http://pen.svn.sourceforge.net/pen/?rev=102&view=rev Author: jstrout Date: 2008-11-30 21:46:54 +0000 (Sun, 30 Nov 2008) Log Message: ----------- Added a new Shelf part type (a blank that is nailed to the wall, so it's a static part). Extended the physics engine to properly handle static parts. Also, extracted most of the Block part type into a new RotatingPart subclass of Part, from which both Block and Shelf derive. Fixed another bug relating to the initialization of rotating parts, so they now appear correct at the right angle in the editor. Modified Paths: -------------- data/parts/blocks.py src/parts.py src/physics.py Added Paths: ----------- data/images/shelf.png data/parts/shelf.py Added: data/images/shelf.png =================================================================== (Binary files differ) Property changes on: data/images/shelf.png ___________________________________________________________________ Added: svn:mime-type + application/octet-stream Modified: data/parts/blocks.py =================================================================== --- data/parts/blocks.py 2008-11-30 20:52:02 UTC (rev 101) +++ data/parts/blocks.py 2008-11-30 21:46:54 UTC (rev 102) @@ -5,11 +5,7 @@ import pymunk from globals import load_image -class Block( Part ): - def __init__( self, type, angle=0, *args, **kwargs ): - Part.__init__( self, type, *args, **kwargs ) - self.angle = int(angle) - self.orig_image = self.image +class Block( parts.RotatingPart ): def get_model( self ): "return (body, shape) tuple that is the physics model of this part" @@ -28,23 +24,6 @@ body.angle = - math.radians(self.angle) return body, shape - def params( self ): - result = Part.params( self ) - result["angle"] = self.angle - return result - - def increment( self ): - self.angle += 15 - self.update_image() - - def decrement( self ): - self.angle -= 15 - self.update_image() - - def update_image( self ): - self.image = pygame.transform.rotate( self.orig_image, self.angle ) - - def create_block_type( name, descr, image, density=None, size=None, bounce=0.3 ): try: image = load_image( image, -1 ) @@ -62,7 +41,7 @@ create_instance = Block) if density is None: density = 2.0 - depth = min(20, max(size)) + depth = min(50, max(size)) volume = size[0] * size[1] * depth type.mass = density * volume type.width = size[0] Added: data/parts/shelf.py =================================================================== --- data/parts/shelf.py (rev 0) +++ data/parts/shelf.py 2008-11-30 21:46:54 UTC (rev 102) @@ -0,0 +1,54 @@ +from __future__ import division +import pygame +import parts +import math +import pymunk +from globals import load_image + +class Shelf( parts.RotatingPart ): + + def get_model( self ): + "return (body, shape) tuple that is the physics model of this part" + width, height, depth = self.type.width, self.type.height, self.type.depth + hw = width/2 + hh = height/2 + points = [(-hw,-hh), (-hw, hh), (hw,hh), (hw, -hh)] + body = pymunk.Body(pymunk.inf, pymunk.inf) + + # note: the model shape for a shelf is not centered on the image, + # due to the brackets. We assume the image is half shelf, half bracket. + shape = pymunk.Poly(body, points, (0,-hh)) + shape.elasticity = self.type.elasticity + shape.friction = 0.8 + + body.angle = - math.radians(self.angle) + return body, shape + +def create_shelf_type( name, descr, image, density=None, size=None, bounce=0.5 ): + try: + image = load_image( image, -1 ) + if size is None: + size = image.get_width(), image.get_height() + except: + if size is None: size = (150, 20) + image = pygame.Surface( size ) + image.set_colorkey( (0,0,0) ) + image.fill( (255,0,0) ) + + type = parts.create_type( name, + image=image, + description = descr, + create_instance = Shelf) + + if density is None: density = 0.9 + depth = min(50, max(size)) + volume = size[0] * size[1] * depth + type.mass = density * volume + type.width = size[0] + type.height = size[1] + type.depth = depth + type.elasticity = bounce + + +create_shelf_type( "Shelf", "A sturdy wall-mounted shelf", image="shelf.png", size=(144,20) ) + Modified: src/parts.py =================================================================== --- src/parts.py 2008-11-30 20:52:02 UTC (rev 101) +++ src/parts.py 2008-11-30 21:46:54 UTC (rev 102) @@ -52,6 +52,35 @@ """ if self.get_model() is None: return self return copy.copy(self) + +class RotatingPart( Part ): + "A subclass of Part for things that can freely rotate (e.g., blocks)." + + def __init__( self, type, angle=0, *args, **kwargs ): + Part.__init__( self, type, *args, **kwargs ) + self.angle = int(angle) + self.orig_image = self.image + self.update_image() + # Set the angle increment -- 15 is a nice choice since it allows + # easy reach of all your common angles: 30, 45, 60, and 90. + self.angle_increment = 15 + + def params( self ): + result = Part.params( self ) + result["angle"] = self.angle + return result + + def increment( self ): + self.angle = (self.angle + self.angle_increment) % 360 + self.update_image() + + def decrement( self ): + self.angle = (self.angle - self.angle_increment) % 360 + self.update_image() + + def update_image( self ): + self.image = pygame.transform.rotate( self.orig_image, self.angle ) + class PartType( object ): Modified: src/physics.py =================================================================== --- src/physics.py 2008-11-30 20:52:02 UTC (rev 101) +++ src/physics.py 2008-11-30 21:46:54 UTC (rev 102) @@ -96,7 +96,7 @@ shape = pymunk.Segment(body, a, b, 2) shape.elasticity = elasticity shape.friction = friction - self.space.add(shape) + self.space.add_static(shape) def add_part(self, part): "Add a part to the physics engine." @@ -105,7 +105,10 @@ if part_model is None: return body, shape = part_model body.position = part.pos - self.space.add(body, shape) + if body.mass >= pymunk.inf: + self.space.add_static(shape) + else: + self.space.add(body, shape) # Store the physics model (i.e. shape) on the sprite, so we can # update it later; and keep a list of them for the same reason This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <js...@us...> - 2008-11-30 20:52:06
|
Revision: 101 http://pen.svn.sourceforge.net/pen/?rev=101&view=rev Author: jstrout Date: 2008-11-30 20:52:02 +0000 (Sun, 30 Nov 2008) Log Message: ----------- Simulation runs no longer mess up the level they're simulating (by allowing each part to create a "simulation part" that is used only for that purpose; in most cases, this will just be a copy of itself, but it could be something different). Also fixed saving and loading of rotation angle in the Block type. Modified Paths: -------------- data/parts/blocks.py src/parts.py src/physics.py Modified: data/parts/blocks.py =================================================================== --- data/parts/blocks.py 2008-11-30 15:02:18 UTC (rev 100) +++ data/parts/blocks.py 2008-11-30 20:52:02 UTC (rev 101) @@ -8,7 +8,7 @@ class Block( Part ): def __init__( self, type, angle=0, *args, **kwargs ): Part.__init__( self, type, *args, **kwargs ) - self.angle = angle + self.angle = int(angle) self.orig_image = self.image def get_model( self ): @@ -28,6 +28,11 @@ body.angle = - math.radians(self.angle) return body, shape + def params( self ): + result = Part.params( self ) + result["angle"] = self.angle + return result + def increment( self ): self.angle += 15 self.update_image() Modified: src/parts.py =================================================================== --- src/parts.py 2008-11-30 15:02:18 UTC (rev 100) +++ src/parts.py 2008-11-30 20:52:02 UTC (rev 101) @@ -1,4 +1,5 @@ from pygame.sprite import Sprite +import copy types = dict() @@ -39,6 +40,19 @@ For the Pymunk engine, this must be a (body, shape) tuple.""" return None + + def get_sim_parts( self ): + """Return a Part or list of Parts to be used for simulation. This should + normally be a copy of this Part, so that it has the same properties and + behavior as the original, but the original is not affected by the + simulation. But it could also just return self if it is a display-only + part that won't be affected by the simulation anyway. The result can be + a list in cases where one edit-time Part must be subdivided into several + Parts for simulation purposes. + """ + if self.get_model() is None: return self + return copy.copy(self) + class PartType( object ): "A PartType object encapsulates the metadata for a type of part." Modified: src/physics.py =================================================================== --- src/physics.py 2008-11-30 15:02:18 UTC (rev 100) +++ src/physics.py 2008-11-30 20:52:02 UTC (rev 101) @@ -108,7 +108,7 @@ self.space.add(body, shape) # Store the physics model (i.e. shape) on the sprite, so we can - # update it later, and keep a list of them for the same reason + # update it later; and keep a list of them for the same reason part.model = shape self.parts.append(part) @@ -143,10 +143,16 @@ add_wall(bg, engine, (5, 0), (5, 500)) add_wall(bg, engine, (795,0), (795, 500)) - # add the parts from the level + # add the simulation parts, generated by the level parts for part in level.parts(): - screengroup.add(part) - engine.add_part(part) + simparts = part.get_sim_parts() + if simparts is None: + continue + if not hasattr(simparts, '__iter__'): + simparts = [simparts] + for simpart in simparts: + screengroup.add(simpart) + engine.add_part(simpart) # do an initial display screen.blit( bg, (0,0) ) This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <js...@us...> - 2008-11-30 15:02:22
|
Revision: 100 http://pen.svn.sourceforge.net/pen/?rev=100&view=rev Author: jstrout Date: 2008-11-30 15:02:18 +0000 (Sun, 30 Nov 2008) Log Message: ----------- Added a new set of Block part types, including both cubes and dominos. These are rotatable in the editor, and of course rotate freely in simulation. Note that there are likely to be a lot of other objects of this sort; we should probably factor out the stuff common to "freely rotating objects" into a Part subclass, so we don't have to repeat it for every other such part type we add. Modified Paths: -------------- data/levels/level1.lev data/parts/balls.py src/parts.py src/physics.py Added Paths: ----------- data/images/block.png data/images/die.png data/images/domino.png data/parts/blocks.py Added: data/images/block.png =================================================================== (Binary files differ) Property changes on: data/images/block.png ___________________________________________________________________ Added: svn:mime-type + application/octet-stream Added: data/images/die.png =================================================================== (Binary files differ) Property changes on: data/images/die.png ___________________________________________________________________ Added: svn:mime-type + application/octet-stream Added: data/images/domino.png =================================================================== (Binary files differ) Property changes on: data/images/domino.png ___________________________________________________________________ Added: svn:mime-type + application/octet-stream Modified: data/levels/level1.lev =================================================================== --- data/levels/level1.lev 2008-11-30 05:29:52 UTC (rev 99) +++ data/levels/level1.lev 2008-11-30 15:02:18 UTC (rev 100) @@ -15,6 +15,8 @@ <part type="Cannon"/> <part type="Anvil" num="2"/> <part type="Balloon" num="2"/> + <part type="Domino" num="12"/> + <part type="Block" num="5"/> </available> </level> Modified: data/parts/balls.py =================================================================== --- data/parts/balls.py 2008-11-30 05:29:52 UTC (rev 99) +++ data/parts/balls.py 2008-11-30 15:02:18 UTC (rev 100) @@ -39,7 +39,7 @@ if density is None: density = 2.0 volume = (4.0/3.0) * math.pi * radius**3.0 - type.mass = density * radius + type.mass = density * volume type.radius = radius type.elasticity = bounce Added: data/parts/blocks.py =================================================================== --- data/parts/blocks.py (rev 0) +++ data/parts/blocks.py 2008-11-30 15:02:18 UTC (rev 100) @@ -0,0 +1,72 @@ +from __future__ import division +import pygame +import parts +import math +import pymunk +from globals import load_image + +class Block( Part ): + def __init__( self, type, angle=0, *args, **kwargs ): + Part.__init__( self, type, *args, **kwargs ) + self.angle = angle + self.orig_image = self.image + + def get_model( self ): + "return (body, shape) tuple that is the physics model of this part" + width, height, depth = self.type.width, self.type.height, self.type.depth + hw = width/2 + hh = height/2 + points = [(-hw,-hh), (-hw, hh), (hw,hh), (hw, -hh)] + mass = self.type.mass + moment = pymunk.moment_for_poly(mass, points, (0,0)) + body = pymunk.Body(mass, moment) + + shape = pymunk.Poly(body, points, (0,0)) + shape.elasticity = self.type.elasticity + shape.friction = 0.8 + + body.angle = - math.radians(self.angle) + return body, shape + + def increment( self ): + self.angle += 15 + self.update_image() + + def decrement( self ): + self.angle -= 15 + self.update_image() + + def update_image( self ): + self.image = pygame.transform.rotate( self.orig_image, self.angle ) + + +def create_block_type( name, descr, image, density=None, size=None, bounce=0.3 ): + try: + image = load_image( image, -1 ) + if size is None: + size = image.get_width(), image.get_height() + except: + if size is None: size = (20, 20) + image = pygame.Surface( size ) + image.set_colorkey( (0,0,0) ) + image.fill( (255,0,0) ) + + type = parts.create_type( name, + image=image, + description = descr, + create_instance = Block) + + if density is None: density = 2.0 + depth = min(20, max(size)) + volume = size[0] * size[1] * depth + type.mass = density * volume + type.width = size[0] + type.height = size[1] + type.depth = depth + type.elasticity = bounce + + +create_block_type( "Block", "A medium-sized wooden block", image="block.png", density=0.9 ) +create_block_type( "Domino", "Great for knocking down", image="domino.png", density=1.2 ) +create_block_type( "Die", "A small hard cube", image="die.png", density=5.0 ) + Modified: src/parts.py =================================================================== --- src/parts.py 2008-11-30 05:29:52 UTC (rev 99) +++ src/parts.py 2008-11-30 15:02:18 UTC (rev 100) @@ -11,7 +11,7 @@ Sprite.__init__( self ) self.type = type - self.pos = (int(x), int(y)) + self.pos = (float(x), float(y)) self.moveable = moveable self.image = self.type.image Modified: src/physics.py =================================================================== --- src/physics.py 2008-11-30 05:29:52 UTC (rev 99) +++ src/physics.py 2008-11-30 15:02:18 UTC (rev 100) @@ -113,8 +113,9 @@ self.parts.append(part) # Also, store a pristine reference to the sprite image, so we can - # rotate it later as needed. - part.orig_image = part.image + # rotate it later as needed. But the sprite may have already done + # this, if it's rotatable; don't override that. + if not hasattr(part, 'orig_image'): part.orig_image = part.image def add_wall(screen, engine, a, b): "Draw a wall on screen from a to b, and also add it to the physics engine." This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <js...@us...> - 2008-11-30 05:29:56
|
Revision: 99 http://pen.svn.sourceforge.net/pen/?rev=99&view=rev Author: jstrout Date: 2008-11-30 05:29:52 +0000 (Sun, 30 Nov 2008) Log Message: ----------- Added a Balloon object. Pymunk doesn't support buoyancy directly, so I've hacked in some code in physics.py to simulate it. The next step will be to generalize that and move it someplace more appropriate. Modified Paths: -------------- data/levels/level1.lev data/parts/balls.py src/physics.py Added Paths: ----------- data/images/balloon.png data/parts/balloon.py Added: data/images/balloon.png =================================================================== (Binary files differ) Property changes on: data/images/balloon.png ___________________________________________________________________ Added: svn:mime-type + application/octet-stream Modified: data/levels/level1.lev =================================================================== --- data/levels/level1.lev 2008-11-30 04:19:56 UTC (rev 98) +++ data/levels/level1.lev 2008-11-30 05:29:52 UTC (rev 99) @@ -14,6 +14,7 @@ <part type="Soccer Ball" num="2"/> <part type="Cannon"/> <part type="Anvil" num="2"/> + <part type="Balloon" num="2"/> </available> </level> Added: data/parts/balloon.py =================================================================== --- data/parts/balloon.py (rev 0) +++ data/parts/balloon.py 2008-11-30 05:29:52 UTC (rev 99) @@ -0,0 +1,23 @@ +from parts import create_type +from globals import load_image + +class Balloon( Part ): + def __init__( self, type, *args, **kwargs ): + Part.__init__( self, type, *args, **kwargs ) + + def get_model( self ): + "return (body, shape) tuple that is the physics model of this part" + radius = 25 + mass = 1 + moment = pymunk.moment_for_circle(mass, 0, radius, (0,0)) + body = pymunk.Body(mass, moment) + + shape = pymunk.Circle(body, radius, (0,0)) + shape.elasticity = 0.8 + shape.friction = 0.4 + + return body, shape + +create_type( "Balloon", description="Filled with helium", image=load_image( "balloon.png",-1 ), + create_instance=Balloon ) + Modified: data/parts/balls.py =================================================================== --- data/parts/balls.py 2008-11-30 04:19:56 UTC (rev 98) +++ data/parts/balls.py 2008-11-30 05:29:52 UTC (rev 99) @@ -14,7 +14,6 @@ mass = self.type.mass moment = pymunk.moment_for_circle(mass, 0, radius, (0,0)) body = pymunk.Body(mass, moment) - body.position = self.pos shape = pymunk.Circle(body, radius, (0,0)) shape.elasticity = self.type.elasticity Modified: src/physics.py =================================================================== --- src/physics.py 2008-11-30 04:19:56 UTC (rev 98) +++ src/physics.py 2008-11-30 05:29:52 UTC (rev 99) @@ -5,6 +5,7 @@ import math import pygame import pymunk +import parts from pygame import sprite from pygame import Rect @@ -80,7 +81,15 @@ body = shape.body part.image = pygame.transform.rotate( part.orig_image, - math.degrees(body.angle) ) part.pos = body.position + + if isinstance(part, parts.Balloon): + # Applying buoyancy is a bit of a PITA. Here's the gist of it; + # TODO: refactor this to something more general. + body.reset_forces() + body.apply_force( (0,-1000), (0,0) ) + body.update_velocity( (0,100), 0.5, 0.02 ) + def add_wall(self, a, b, elasticity=0.7, friction=0.5): "Add a wall to the physics model, with endpoints at a and b." body = pymunk.Body(inf, inf) @@ -106,7 +115,6 @@ # Also, store a pristine reference to the sprite image, so we can # rotate it later as needed. part.orig_image = part.image - def add_wall(screen, engine, a, b): "Draw a wall on screen from a to b, and also add it to the physics engine." @@ -129,7 +137,8 @@ # create the walls bg = paper.copy() - add_wall(bg, engine, (0, 500), (800, 500)) + add_wall(bg, engine, (0, 0), (800, 0)) + add_wall(bg, engine, (0, 500), (800, 500)) add_wall(bg, engine, (5, 0), (5, 500)) add_wall(bg, engine, (795,0), (795, 500)) This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <js...@us...> - 2008-11-30 04:19:58
|
Revision: 98 http://pen.svn.sourceforge.net/pen/?rev=98&view=rev Author: jstrout Date: 2008-11-30 04:19:56 +0000 (Sun, 30 Nov 2008) Log Message: ----------- Added the "bonk" sound, as well as the anvil model. Modified Paths: -------------- data/parts/anvil.py data/parts/balls.py src/physics.py Added Paths: ----------- data/sounds/bonk.wav Modified: data/parts/anvil.py =================================================================== --- data/parts/anvil.py 2008-11-30 04:01:09 UTC (rev 97) +++ data/parts/anvil.py 2008-11-30 04:19:56 UTC (rev 98) @@ -1,5 +1,25 @@ from parts import create_type from globals import load_image -create_type( "Anvil", description="Glorified paperweight", image=load_image( "anvil.png",-1 ) ) +class Anvil( Part ): + def __init__( self, type, *args, **kwargs ): + Part.__init__( self, type, *args, **kwargs ) + def get_model( self ): + "return (body, shape) tuple that is the physics model of this part" + # br tr tl bl + verts = [(36, 23), (21, -16), (-19, -16), (-37, 23)] + mass = 1000 + moment = pymunk.moment_for_poly(mass, verts, (0,0)) + + body = pymunk.Body(mass, moment) + + shape = pymunk.Poly(body, verts, (0,0)) + shape.elasticity = 0.1 + shape.friction = 0.5 + + return body, shape + +create_type( "Anvil", description="Glorified paperweight", image=load_image( "anvil.png",-1 ), + create_instance=Anvil ) + Modified: data/parts/balls.py =================================================================== --- data/parts/balls.py 2008-11-30 04:01:09 UTC (rev 97) +++ data/parts/balls.py 2008-11-30 04:19:56 UTC (rev 98) @@ -1,18 +1,49 @@ import pygame import parts +import math +import pymunk from globals import load_image -def create_ball_type( name, descr, image, mass=None, density=None, radius=20, bounce=0.5 ): +class Ball( Part ): + def __init__( self, type, *args, **kwargs ): + Part.__init__( self, type, *args, **kwargs ) + + def get_model( self ): + "return (body, shape) tuple that is the physics model of this part" + radius = self.type.radius + mass = self.type.mass + moment = pymunk.moment_for_circle(mass, 0, radius, (0,0)) + body = pymunk.Body(mass, moment) + body.position = self.pos + + shape = pymunk.Circle(body, radius, (0,0)) + shape.elasticity = self.type.elasticity + shape.friction = 0.8 + + return body, shape + + +def create_ball_type( name, descr, image, density=None, radius=None, bounce=0.5 ): try: image = load_image( image, -1 ) + if radius is None: radius = image.get_width() * 0.5 except: + if radius is None: radius = 20 image = pygame.Surface( (2*radius,2*radius) ) image.set_colorkey( (0,0,0) ) pygame.draw.circle( image, (255,0,0), (radius,radius), radius ) - parts.create_type( name, + type = parts.create_type( name, image=image, - description=descr ) + description = descr, + create_instance = Ball) + + if density is None: density = 2.0 + volume = (4.0/3.0) * math.pi * radius**3.0 + type.mass = density * radius + type.radius = radius + type.elasticity = bounce + create_ball_type( "Soccer Ball", "A large but light ball", image="soccerball.png", density=0.5, bounce=0.7 ) create_ball_type( "Tennis Ball", "A small bouncy ball", image="tennisball.png", density=0.8, bounce=0.9 ) Added: data/sounds/bonk.wav =================================================================== (Binary files differ) Property changes on: data/sounds/bonk.wav ___________________________________________________________________ Added: svn:mime-type + application/octet-stream Modified: src/physics.py =================================================================== --- src/physics.py 2008-11-30 04:01:09 UTC (rev 97) +++ src/physics.py 2008-11-30 04:19:56 UTC (rev 98) @@ -95,6 +95,7 @@ part_model = part.get_model() if part_model is None: return body, shape = part_model + body.position = part.pos self.space.add(body, shape) # Store the physics model (i.e. shape) on the sprite, so we can This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <js...@us...> - 2008-11-30 04:01:14
|
Revision: 97 http://pen.svn.sourceforge.net/pen/?rev=97&view=rev Author: jstrout Date: 2008-11-30 04:01:09 +0000 (Sun, 30 Nov 2008) Log Message: ----------- Implemented the guts of the physics model; currently only supports balls, but other part types won't be too hard to add. Includes some simple sound effects (which should probably get split into its own module). Modified Paths: -------------- src/globals.py src/parts.py src/physics.py Modified: src/globals.py =================================================================== --- src/globals.py 2008-11-30 00:17:29 UTC (rev 96) +++ src/globals.py 2008-11-30 04:01:09 UTC (rev 97) @@ -30,6 +30,18 @@ print 'cannot load font: ', name raise SystemExit + +def load_sound(name): + try: + filename = os.path.join( 'sounds', name ) + file = files.open( filename ) + # TODO: .name is a workaround + sound = pygame.mixer.Sound( file.name ) + return sound + except: + print 'cannot load sound:', name + raise SystemExit + # global variables pygame.display.init() pygame.font.init() Modified: src/parts.py =================================================================== --- src/parts.py 2008-11-30 00:17:29 UTC (rev 96) +++ src/parts.py 2008-11-30 04:01:09 UTC (rev 97) @@ -32,6 +32,13 @@ def params( self ): return {'x': self.pos[0], 'y': self.pos[1]} + + def get_model( self ): + """Return the physics model for this part. If you return None, then it will be + a display-only part that does not participate in the physics simulation at all. + + For the Pymunk engine, this must be a (body, shape) tuple.""" + return None class PartType( object ): "A PartType object encapsulates the metadata for a type of part." @@ -41,7 +48,8 @@ image, fixed = True, create_instance = Part, - description = "No description." ): + description = "No description.", + ): self.name = name self.image = image @@ -59,7 +67,10 @@ types[type.name] = type def create_type( *args, **kwargs ): - register( PartType( *args, **kwargs ) ) + "Create a part type, register it with the global list, and return it." + type = PartType( *args, **kwargs ) + register(type) + return type # TODO: is this in the right place? import files Modified: src/physics.py =================================================================== --- src/physics.py 2008-11-30 00:17:29 UTC (rev 96) +++ src/physics.py 2008-11-30 04:01:09 UTC (rev 97) @@ -1,8 +1,170 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +from __future__ import division +import math +import pygame +import pymunk + +from pygame import sprite +from pygame import Rect +from pymunk import inf + +from globals import load_image, load_sound + + +class SoundEngine(): + """Simple class responsible for making noise reflecting the simulation. + The physics engine should just call this class whenever something interesting + happens; it will play a sound as appropriate.""" + + def __init__(self): + pygame.mixer.init() + self.bonk_sound = load_sound('bonk.wav') + self.last_collisions = {} # key: (shapeA, shapeB); value: True + self.next_collisions = {} # same, but for next frame + + def pre_update(self): + "Call this before updating the physics simulation." + pass + + def post_update(self): + "Call this after updating the physics simulation." + self.last_collisions = self.next_collisions + self.next_collisions = {} + + def collision(self, shapeA, shapeB, contacts, normal_coef): + "Play a sound appropriate for the given collision." + if not contacts: return + c = contacts[0] # for now, just use the first contact + if (shapeA, shapeB) not in self.last_collisions: + channel = self.bonk_sound.play() + if channel is not None: + volume = c.distance ** 2.0 + pan = c.position[0] / 800.0 + channel.set_volume(volume * (1.0 - pan), volume * pan) + self.next_collisions[ (shapeA, shapeB) ] = True + +soundEngine = SoundEngine() + +def collision_func(shapeA, shapeB, contacts, normal_coef, data): + """Callback function for pymunk collisions.""" + soundEngine.collision(shapeA, shapeB, contacts, normal_coef) + return True + +class PhysicsEngine(): + """Abstracts our physics engine as much as possible, so the gory details + of talking to pymunk (or whatever else we choose) are encapsulated.""" + + def __init__(self, gravity=100.0): + pymunk.init_pymunk() + self.space = pymunk.Space() + self.space.gravity = (0, gravity) + self.space._space.contents.elasticIterations = 10 + self.space.set_default_collisionpair_func(collision_func) + self.parts = [] + + + def update(self, dt, substeps=10): + "Update the physics model for the given amount of simulated time." + subdt = dt / substeps + for x in range(substeps): + self.space.step( subdt ) + + for part in self.parts: + self.update_sprite(part) + + def update_sprite(self, part): + "Update the given sprite image and position to match its physics model." + shape = part.model + body = shape.body + part.image = pygame.transform.rotate( part.orig_image, - math.degrees(body.angle) ) + part.pos = body.position + + def add_wall(self, a, b, elasticity=0.7, friction=0.5): + "Add a wall to the physics model, with endpoints at a and b." + body = pymunk.Body(inf, inf) + shape = pymunk.Segment(body, a, b, 2) + shape.elasticity = elasticity + shape.friction = friction + self.space.add(shape) + + def add_part(self, part): + "Add a part to the physics engine." + # Add the body and shape to the physics engine. + part_model = part.get_model() + if part_model is None: return + body, shape = part_model + self.space.add(body, shape) + + # Store the physics model (i.e. shape) on the sprite, so we can + # update it later, and keep a list of them for the same reason + part.model = shape + self.parts.append(part) + + # Also, store a pristine reference to the sprite image, so we can + # rotate it later as needed. + part.orig_image = part.image + + +def add_wall(screen, engine, a, b): + "Draw a wall on screen from a to b, and also add it to the physics engine." + engine.add_wall(a, b) + pygame.draw.line(screen, (0,0,0), a, b) + def simulate( level ): - pass + "Main simulation loop -- run and display a simulation for the given level." + from globals import paper, screen + # set up pygame + pygame.mouse.set_visible( 0 ) + pygame.event.set_blocked( None ) + pygame.event.set_allowed( pygame.constants.KEYUP ) + pygame.event.set_allowed( pygame.constants.QUIT ) + screengroup = sprite.RenderUpdates() # main group, all visible sprites + # set up the physics engine + engine = PhysicsEngine() + + # create the walls + bg = paper.copy() + add_wall(bg, engine, (0, 500), (800, 500)) + add_wall(bg, engine, (5, 0), (5, 500)) + add_wall(bg, engine, (795,0), (795, 500)) + + # add the parts from the level + for part in level.parts(): + screengroup.add(part) + engine.add_part(part) + + # do an initial display + screen.blit( bg, (0,0) ) + pygame.display.update() + + # enter the main event/update loop + clock = pygame.time.Clock() + while True: + event = pygame.event.poll() + + if event.type == pygame.constants.QUIT: + break + elif event.type == pygame.constants.KEYUP: + break + + clock.tick() + soundEngine.pre_update() + dt = 0.02 + engine.update(dt) + soundEngine.post_update() + + screengroup.update( dt ) + screengroup.clear( screen, bg ) + pygame.display.update( screengroup.draw( screen ) ) + + pygame.mouse.set_visible( 1 ) + + + ## Testing ##################################################################### if __name__ == '__main__': This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <mdg...@us...> - 2008-11-30 00:17:34
|
Revision: 96 http://pen.svn.sourceforge.net/pen/?rev=96&view=rev Author: mdgeorge Date: 2008-11-30 00:17:29 +0000 (Sun, 30 Nov 2008) Log Message: ----------- Added call into physics module. Mostly just testing pen-commits list Modified Paths: -------------- src/editor.py Modified: src/editor.py =================================================================== --- src/editor.py 2008-11-29 23:33:15 UTC (rev 95) +++ src/editor.py 2008-11-30 00:17:29 UTC (rev 96) @@ -8,6 +8,7 @@ _Solver( level ).run() def multi( level ): + # Multiplayer _Multi( level ).run() ## Implementation ############################################################## @@ -402,9 +403,8 @@ self._dirty = [(0,0,800,600)] def _run( self ): - # TODO - from demos import penode - penode.rundemo() + import physics + physics.simulate(self._level) self._setup() def _update( self, type ): This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |