Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWalter Bender <walter@sugarlabs.org>2014-03-07 19:44:11 (GMT)
committer Walter Bender <walter@sugarlabs.org>2014-03-07 19:44:11 (GMT)
commit8ba9d4ae100be129b255230790149f488032fa3c (patch)
tree791c00f957b6a04c8fd32e1c8059ce1e690b8ee7
parent3d36a1c34a7c49f4265bb669aefcacededc94e55 (diff)
elements cleanupgtk3
-rw-r--r--NEWS6
-rwxr-xr-xactivity/activity.info2
-rw-r--r--elements/add_objects.py350
-rw-r--r--elements/callbacks.py67
-rw-r--r--elements/camera.py72
-rw-r--r--elements/drawing.py159
-rw-r--r--elements/elements.py307
-rw-r--r--elements/locals.py10
-rw-r--r--elements/menu.py152
-rw-r--r--elements/tools.py35
-rw-r--r--elements/tools_poly.py266
11 files changed, 778 insertions, 648 deletions
diff --git a/NEWS b/NEWS
index bf05f34..81b0f46 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,9 @@
+20
+
+* pep8 cleanup of Elements package
+* Added more robust error checks in Elements
+* Fixed color conversion bug in Elements
+
19
BUG FIX:
diff --git a/activity/activity.info b/activity/activity.info
index 19ebcbf..537b8ff 100755
--- a/activity/activity.info
+++ b/activity/activity.info
@@ -4,6 +4,6 @@ summary = Prove Sir Isaac Newton right! Create real life simulations using diffe
bundle_id = org.laptop.physics
exec = sugar-activity activity.PhysicsActivity
icon = activity-physics
-activity_version = 19
+activity_version = 20
show_launcher = yes
mime_types = application/x-physics-activity;
diff --git a/elements/add_objects.py b/elements/add_objects.py
index bfb7e0b..cec8212 100644
--- a/elements/add_objects.py
+++ b/elements/add_objects.py
@@ -8,7 +8,7 @@ Home: http://elements.linuxuser.at
IRC: #elements on irc.freenode.org
Code: http://www.assembla.com/wiki/show/elements
- svn co http://svn2.assembla.com/svn/elements
+ svn co http://svn2.assembla.com/svn/elements
License: GPLv3 | See LICENSE for the full text
This program is free software: you can redistribute it and/or modify
@@ -22,7 +22,7 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
-along with this program. If not, see <http://www.gnu.org/licenses/>.
+along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
from locals import *
from elements import box2d
@@ -34,64 +34,75 @@ from math import asin
import tools_poly
+
class Add:
element_count = 0
-
+
def __init__(self, parent):
self.parent = parent
-
+
def ground(self):
""" Add a static ground to the scene
-
+
Return: box2d.b2Body
"""
return self._rect((-10.0, 0.0), 50.0, 0.1, dynamic=False)
-
- def triangle(self, pos, sidelength, dynamic=True, density=1.0, restitution=0.16, friction=0.5, screenCoord=True):
- """ Add a triangle | pos & a in the current input unit system (meters or pixels)
-
+
+ def triangle(self, pos, sidelength, dynamic=True, density=1.0,
+ restitution=0.16, friction=0.5, screenCoord=True):
+ """ Add a triangle | pos & a in the current input unit system
+ (meters or pixels)
+
Parameters:
pos .... position (x,y)
sidelength ...... sidelength
other .. see [physics parameters]
-
+
Return: box2d.b2Body
"""
- vertices = [(-sidelength, 0.0), (sidelength, 0.0), (0.0, 2*sidelength)]
- return self.poly(pos, vertices, dynamic, density, restitution, friction, screenCoord)
+ vertices = [(-sidelength, 0.0), (sidelength, 0.0),
+ (0.0, 2 * sidelength)]
+ return self.poly(pos, vertices, dynamic, density, restitution,
+ friction, screenCoord)
+
+ def ball(self, pos, radius, dynamic=True, density=1.0, restitution=0.16,
+ friction=0.5, screenCoord=True):
+ """ Add a dynamic ball at pos after correcting the positions and
+ lengths to the internal meter system if neccessary
+ (if INPUT_PIXELS), then call self._add_ball(...)
- def ball(self, pos, radius, dynamic=True, density=1.0, restitution=0.16, friction=0.5, screenCoord=True):
- """ Add a dynamic ball at pos after correcting the positions and legths to the internal
- meter system if neccessary (if INPUT_PIXELS), then call self._add_ball(...)
-
Parameters:
pos ..... position (x,y)
radius .. circle radius
other ... see [physics parameters]
-
+
Return: box2d.b2Body
"""
- # Bring coordinates into the world coordinate system (flip, camera offset, ...)
- if screenCoord: x, y = self.parent.to_world(pos)
- else: x, y = pos
-
+ # Bring coordinates into the world coordinate system (flip,
+ # camera offset, ...)
+ if screenCoord:
+ x, y = self.parent.to_world(pos)
+ else:
+ x, y = pos
- if self.parent.input == INPUT_PIXELS:
+ if self.parent.input_unit == INPUT_PIXELS:
x /= self.parent.ppm
y /= self.parent.ppm
radius /= self.parent.ppm
-
- return self._ball((x,y), radius, dynamic, density, restitution, friction)
- def _ball(self, pos, radius, dynamic=True, density=1.0, restitution=0.16, friction=0.5):
+ return self._ball((x, y), radius, dynamic, density, restitution,
+ friction)
+
+ def _ball(self, pos, radius, dynamic=True, density=1.0, restitution=0.16,
+ friction=0.5):
# Add a ball without correcting any settings
# meaning, pos and vertices are in meters
# Define the body
x, y = pos
bodyDef = box2d.b2BodyDef()
- bodyDef.position=(x, y)
+ bodyDef.position = (x, y)
- userData = { 'color' : self.parent.get_color() }
+ userData = {'color': self.parent.get_color()}
bodyDef.userData = userData
# Create the Body
@@ -99,7 +110,7 @@ class Add:
density = 0
body = self.parent.world.CreateBody(bodyDef)
-
+
self.parent.element_count += 1
# Add a shape to the Body
@@ -110,12 +121,14 @@ class Add:
circleDef.friction = friction
body.CreateShape(circleDef)
- body.SetMassFromShapes()
-
- return body
+ body.SetMassFromShapes()
+
+ return body
- def rect(self, pos, width, height, angle=0, dynamic=True, density=1.0, restitution=0.16, friction=0.5, screenCoord=True):
- """ Add a dynamic rectangle with input unit according to self.input (INPUT_PIXELS or INPUT_METERS)
+ def rect(self, pos, width, height, angle=0, dynamic=True, density=1.0,
+ restitution=0.16, friction=0.5, screenCoord=True):
+ """ Add a dynamic rectangle with input unit according to self.input
+ (INPUT_PIXELS or INPUT_METERS)
Correcting the positions to meters and calling self._add_rect()
Parameters:
@@ -127,69 +140,78 @@ class Add:
Return: box2d.b2Body
"""
- # Bring coordinates into the world coordinate system (flip, camera offset, ...)
- if screenCoord: x, y = self.parent.to_world(pos)
- else: x, y = pos
+ # Bring coordinates into the world coordinate system (flip,
+ # camera offset, ...)
+ if screenCoord:
+ x, y = self.parent.to_world(pos)
+ else:
+ x, y = pos
# If required, translate pixel -> meters
- if self.parent.input == INPUT_PIXELS:
+ if self.parent.input_unit == INPUT_PIXELS:
x /= self.parent.ppm
y /= self.parent.ppm
width /= self.parent.ppm
height /= self.parent.ppm
-
+
# grad -> radians
angle = (angle * pi) / 180
-
- return self._rect((x,y), width, height, angle, dynamic, density, restitution, friction)
+ return self._rect((x, y), width, height, angle, dynamic, density,
+ restitution, friction)
- def wall(self, pos1, pos2, width=5, density=1.0, restitution=0.16, friction=0.5, screenCoord=True):
- """ Add a static rectangle between two arbitrary points with input unit according to self.input
- (INPUT_PIXELS or INPUT_METERS) Correcting the positions to meters and calling self._add_rect()
+ def wall(self, pos1, pos2, width=5, density=1.0, restitution=0.16,
+ friction=0.5, screenCoord=True):
+ """ Add a static rectangle between two arbitrary points with input
+ unit according to self.input_unit (INPUT_PIXELS or INPUT_METERS)
+ Correcting the positions to meters and calling self._add_rect()
Return: box2d.b2Body
"""
- if width < 5: width = 5
-
- if (pos1[0] < pos2[0]):
+ if width < 5:
+ width = 5
+
+ if (pos1[0] < pos2[0]):
x1, y1 = pos1
x2, y2 = pos2
else:
x1, y1 = pos2
- x2, y2 = pos1
+ x2, y2 = pos1
- # Bring coordinates into the world coordinate system (flip, camera offset, ...)
- if screenCoord:
- x1, y1 = self.parent.to_world((x1, y1))
- x2, y2 = self.parent.to_world((x2, y2))
+ # Bring coordinates into the world coordinate system (flip,
+ # camera offset, ...)
+ if screenCoord:
+ x1, y1 = self.parent.to_world((x1, y1))
+ x2, y2 = self.parent.to_world((x2, y2))
# If required, translate pixel -> meters
- if self.parent.input == INPUT_PIXELS:
+ if self.parent.input_unit == INPUT_PIXELS:
x1 /= self.parent.ppm
y1 /= self.parent.ppm
x2 /= self.parent.ppm
y2 /= self.parent.ppm
width /= self.parent.ppm
-
- length = sqrt( (x1-x2)*(x1-x2) + (y1-y2)*(y1-y2) )*0.5
+
+ length = sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2)) * 0.5
if width > 0:
- halfX = x1 + (x2-x1)*0.5
- halfY = y1 + (y2-y1)*0.5
+ halfX = x1 + (x2 - x1) * 0.5
+ halfY = y1 + (y2 - y1) * 0.5
+
+ angle = asin((y2 - halfY) / length)
+ return self._rect((halfX, halfY), length, width, angle, False,
+ density, restitution, friction)
- angle = asin( (y2-halfY)/length )
- return self._rect((halfX, halfY), length, width, angle, False, density, restitution, friction)
-
- def _rect(self, pos, width, height, angle=0, dynamic=True, density=1.0, restitution=0.16, friction=0.5):
+ def _rect(self, pos, width, height, angle=0, dynamic=True, density=1.0,
+ restitution=0.16, friction=0.5):
# Add a rect without correcting any settings
# meaning, pos and vertices are in meters
# angle is now in radians ((degrees * pi) / 180))
x, y = pos
bodyDef = box2d.b2BodyDef()
- bodyDef.position=(x, y)
+ bodyDef.position = (x, y)
- userData = { 'color' : self.parent.get_color() }
+ userData = {'color': self.parent.get_color()}
bodyDef.userData = userData
# Create the Body
@@ -197,25 +219,28 @@ class Add:
density = 0
body = self.parent.world.CreateBody(bodyDef)
-
+
self.parent.element_count += 1
# Add a shape to the Body
boxDef = box2d.b2PolygonDef()
-
- boxDef.SetAsBox(width, height, (0,0), angle)
+
+ boxDef.SetAsBox(width, height, (0, 0), angle)
boxDef.density = density
boxDef.restitution = restitution
boxDef.friction = friction
body.CreateShape(boxDef)
-
+
body.SetMassFromShapes()
-
+
return body
- def poly(self, pos, vertices, dynamic=True, density=1.0, restitution=0.16, friction=0.5, screenCoord=True):
- """ Add a dynamic polygon, which has the vertices arranged around the poly's center at pos
- Correcting the positions to meters if INPUT_PIXELS, and calling self._add_poly()
+ def poly(self, pos, vertices, dynamic=True, density=1.0, restitution=0.16,
+ friction=0.5, screenCoord=True):
+ """ Add a dynamic polygon, which has the vertices arranged around
+ the poly's center at pos
+ Correcting the positions to meters if INPUT_PIXELS, and calling
+ self._add_poly()
Parameters:
pos ....... position (x,y)
@@ -223,34 +248,39 @@ class Add:
other ... see [physics parameters]
Return: box2d.b2Body
- """
- # Bring coordinates into the world coordinate system (flip, camera offset, ...)
- if screenCoord: x, y = self.parent.to_world(pos)
- else: x, y = pos
-
+ """
+ # Bring coordinates into the world coordinate system (flip,
+ # camera offset, ...)
+ if screenCoord:
+ x, y = self.parent.to_world(pos)
+ else:
+ x, y = pos
+
# If required, translate pixel -> meters
- if self.parent.input == INPUT_PIXELS:
+ if self.parent.input_unit == INPUT_PIXELS:
# translate pixel -> meters
x /= self.parent.ppm
y /= self.parent.ppm
-
+
# Translate vertices from pixels to meters
v_new = []
for v in vertices:
vx, vy = v
- v_new.append((vx/self.parent.ppm, vy/self.parent.ppm))
+ v_new.append((vx / self.parent.ppm, vy / self.parent.ppm))
vertices = v_new
-
- return self._poly((x,y), vertices, dynamic, density, restitution, friction)
- def _poly(self, pos, vertices, dynamic=True, density=1.0, restitution=0.16, friction=0.5):
+ return self._poly((x, y), vertices, dynamic, density, restitution,
+ friction)
+
+ def _poly(self, pos, vertices, dynamic=True, density=1.0,
+ restitution=0.16, friction=0.5):
# add a centered poly at pos without correcting any settings
# meaning, pos and vertices are in meters
x, y = pos
bodyDef = box2d.b2BodyDef()
- bodyDef.position=(x, y)
-
- userData = { 'color' : self.parent.get_color() }
+ bodyDef.position = (x, y)
+
+ userData = {'color': self.parent.get_color()}
bodyDef.userData = userData
# Create the Body
@@ -258,7 +288,7 @@ class Add:
density = 0
body = self.parent.world.CreateBody(bodyDef)
-
+
self.parent.element_count += 1
# Add a shape to the Body
@@ -271,36 +301,40 @@ class Add:
body.CreateShape(polyDef)
body.SetMassFromShapes()
-
+
return body
-
- def concavePoly(self, vertices, dynamic=True, density=1.0, restitution=0.16, friction=0.5, screenCoord=True):
- # 1. Step: Reduce
+
+ def concavePoly(self, vertices, dynamic=True, density=1.0,
+ restitution=0.16, friction=0.5, screenCoord=True):
+ # 1. Step: Reduce
# Detect if the polygon is closed or open
- if vertices[0] != vertices[-1]:
+ if vertices[0] != vertices[-1]:
is_closed = False
else:
is_closed = True
-
+
# Continue reducing the vertecs
x, y = c = tools_poly.calc_center(vertices)
vertices = tools_poly.poly_center_vertices(vertices)
-
- # Bring coordinates into the world coordinate system (flip, camera offset, ...)
- if screenCoord: x, y = self.parent.to_world(c)
- else: x, y = c
+
+ # Bring coordinates into the world coordinate system (flip,
+ # camera offset, ...)
+ if screenCoord:
+ x, y = self.parent.to_world(c)
+ else:
+ x, y = c
# If required, translate pixel -> meters
- if self.parent.input == INPUT_PIXELS:
+ if self.parent.input_unit == INPUT_PIXELS:
# translate pixel -> meters
x /= self.parent.ppm
y /= self.parent.ppm
-
+
# Let's add the body
bodyDef = box2d.b2BodyDef()
- bodyDef.position=(x, y)
+ bodyDef.position = (x, y)
- userData = { 'color' : self.parent.get_color() }
+ userData = {'color': self.parent.get_color()}
bodyDef.userData = userData
# Create the Body
@@ -308,12 +342,12 @@ class Add:
density = 0
body = self.parent.world.CreateBody(bodyDef)
-
+
self.parent.element_count += 1
# Create the reusable Box2D polygon and circle definitions
polyDef = box2d.b2PolygonDef()
- polyDef.vertexCount = 4 # rectangle
+ polyDef.vertexCount = 4 # rectangle
polyDef.density = density
polyDef.restitution = restitution
polyDef.friction = friction
@@ -324,25 +358,25 @@ class Add:
circleDef.restitution = restitution
circleDef.friction = friction
- # Set the scale factor
+ # Set the scale factor
factor = 8.0
v2 = box2d.b2Vec2(*vertices[0])
for v in vertices[1:]:
- v1 = v2.copy()
- v2 = box2d.b2Vec2(*v)
-
- vdir = v2-v1 # (v2x-v1x, v2y-v1y)
+ v1 = v2.copy()
+ v2 = box2d.b2Vec2(*v)
+
+ vdir = v2 - v1 # (v2x-v1x, v2y-v1y)
vdir.Normalize()
-
+
# we need a little size for the end part
- vn = box2d.b2Vec2(-vdir.y*factor, vdir.x*factor)
+ vn = box2d.b2Vec2(-vdir.y * factor, vdir.x * factor)
- v = [ v1+vn, v1-vn, v2-vn, v2+vn ]
+ v = [v1 + vn, v1 - vn, v2 - vn, v2 + vn]
- # Create a line (rect) for each part of the polygon,
- # and attach it to the body
- polyDef.setVertices( [vi / self.parent.ppm for vi in v] )
+ # Create a line (rect) for each part of the polygon,
+ # and attach it to the body
+ polyDef.setVertices([vi / self.parent.ppm for vi in v])
try:
polyDef.checkValues()
@@ -359,20 +393,21 @@ class Add:
break
circleDef.localPosition = v2 / self.parent.ppm
- body.CreateShape(circleDef)
-
+ body.CreateShape(circleDef)
+
# Now, all shapes have been attached
- body.SetMassFromShapes()
-
+ body.SetMassFromShapes()
+
# Return hard and soft reduced vertices
return body
- def complexPoly(self, vertices, dynamic=True, density=1.0, restitution=0.16, friction=0.5):
+ def complexPoly(self, vertices, dynamic=True, density=1.0,
+ restitution=0.16, friction=0.5):
# 1. Step: Reduce
# 2. Step: See if start and end are close, if so then close the polygon
# 3. Step: Detect if convex or concave
# 4. Step: Start self.convexPoly or self.concavePoly
- vertices, is_convex = tools_poly.reduce_poly_by_angle(vertices)
+ vertices, is_convex = tools_poly.reduce_poly_by_angle(vertices)
#print "->", is_convex
# If start and endpoints are close to each other, close polygon
@@ -380,7 +415,7 @@ class Add:
x2, y2 = vertices[-1]
dx = x2 - x1
dy = y2 - y1
- l = sqrt((dx*dx)+(dy*dy))
+ l = sqrt((dx * dx) + (dy * dy))
if l < 50:
vertices[-1] = vertices[0]
@@ -392,19 +427,22 @@ class Add:
# Lines shall be drawn by self.concavePoly(...)
# print "is line"
is_convex = False
-
+
if is_convex:
# print "convex"
- return self.convexPoly(vertices, dynamic, density, restitution, friction), vertices
+ return self.convexPoly(vertices, dynamic, density, restitution,
+ friction), vertices
else:
# print "concave"
- return self.concavePoly(vertices, dynamic, density, restitution, friction), vertices
-
+ return self.concavePoly(vertices, dynamic, density, restitution,
+ friction), vertices
- def convexPoly(self, vertices, dynamic=True, density=1.0, restitution=0.16, friction=0.5):
- """ Add a complex polygon with vertices in absolute positions (meters or pixels, according
- to INPUT_PIXELS or INPUT_METERS). This function does the reduction and convec hulling
- of the poly, and calls add_poly(...)
+ def convexPoly(self, vertices, dynamic=True, density=1.0,
+ restitution=0.16, friction=0.5):
+ """ Add a complex polygon with vertices in absolute positions
+ (meters or pixels, according to INPUT_PIXELS or INPUT_METERS).
+ This function does the reduction and convec hulling
+ of the poly, and calls add_poly(...)
Parameters:
vertices .. absolute vertices positions
@@ -412,31 +450,34 @@ class Add:
Return: box2d.b2Body
"""
- # NOTE: Box2D has a maximum poly vertex count, defined in Common/box2d.b2Settings.h (box2d.b2_maxPolygonVertices)
- # We need to make sure, that we reach that by reducing the poly with increased tolerance
- # Reduce Polygon
- tolerance = 10 #5
+ # NOTE: Box2D has a maximum poly vertex count, defined in
+ # Common/box2d.b2Settings.h (box2d.b2_maxPolygonVertices) We
+ # need to make sure, that we reach that by reducing the poly
+ # with increased tolerance Reduce Polygon
+ tolerance = 10 # 5
v_new = vertices
while len(v_new) > box2d.b2_maxPolygonVertices:
- tolerance += 1
+ tolerance += 1
v_new = tools_poly.reduce_poly(vertices, tolerance)
-
- # print "convexPoly: Polygon reduced from %i to %i vertices | tolerance: %i" % (len(vertices), len(v_new), tolerance)
+
+ # print "convexPoly: Polygon reduced from %i to %i vertices |
+ # tolerance: %i" % (len(vertices), len(v_new), tolerance)
vertices = v_new
-
+
# So poly should be alright now
# Continue reducing the vertecs
- vertices_orig_reduced = vertices
+ vertices_orig_reduced = vertices
vertices = tools_poly.poly_center_vertices(vertices)
vertices = tools_poly.convex_hull(vertices)
- if len(vertices) < 3:
- return
-
+ if len(vertices) < 3:
+ return
+
# Define the body
- x, y = c = tools_poly.calc_center(vertices_orig_reduced)
- return self.poly((x,y), vertices, dynamic, density, restitution, friction)
+ x, y = tools_poly.calc_center(vertices_orig_reduced)
+ return self.poly((x, y), vertices, dynamic, density, restitution,
+ friction)
def to_b2vec(self, pt):
# Convert vector to a b2vect
@@ -454,33 +495,33 @@ class Add:
b1, b2, p1, p2, flag = args
p1 = self.to_b2vec(p1)
- p2 = self.to_b2vec(p2)
-
+ p2 = self.to_b2vec(p2)
+
jointDef = box2d.b2DistanceJointDef()
jointDef.Initialize(b1, b2, p1, p2)
jointDef.collideConnected = flag
-
- self.parent.world.CreateJoint(jointDef)
+
+ self.parent.world.CreateJoint(jointDef)
elif len(args) == 4:
# Distance Joint
b1, b2, p1, p2 = args
p1 = self.to_b2vec(p1)
- p2 = self.to_b2vec(p2)
-
+ p2 = self.to_b2vec(p2)
+
jointDef = box2d.b2DistanceJointDef()
jointDef.Initialize(b1, b2, p1, p2)
jointDef.collideConnected = True
-
- self.parent.world.CreateJoint(jointDef)
-
+
+ self.parent.world.CreateJoint(jointDef)
+
elif len(args) == 3:
# Revolute Joint between two bodies (unimplemented)
pass
elif len(args) == 2:
- # Revolute Joint to the Background, at point
+ # Revolute Joint to the Background, at point
b1 = self.parent.world.GetGroundBody()
b2 = args[0]
p1 = self.to_b2vec(args[1])
@@ -494,10 +535,10 @@ class Add:
b1 = self.parent.world.GetGroundBody()
b2 = args[0]
p1 = b2.GetWorldCenter()
-
+
jointDef = box2d.b2RevoluteJointDef()
jointDef.Initialize(b1, b2, p1)
-
+
self.parent.world.CreateJoint(jointDef)
def motor(self, body, pt, torque=900, speed=-10):
@@ -530,9 +571,8 @@ class Add:
else:
self.parent.mouseJoint = self.parent.world.CreateJoint(mj)
body.WakeUp()
-
+
def remove_mouseJoint(self):
if self.parent.mouseJoint:
self.parent.world.DestroyJoint(self.parent.mouseJoint)
self.parent.mouseJoint = None
-
diff --git a/elements/callbacks.py b/elements/callbacks.py
index 01e9545..d3aae96 100644
--- a/elements/callbacks.py
+++ b/elements/callbacks.py
@@ -8,7 +8,7 @@ Home: http://elements.linuxuser.at
IRC: #elements on irc.freenode.org
Code: http://www.assembla.com/wiki/show/elements
- svn co http://svn2.assembla.com/svn/elements
+ svn co http://svn2.assembla.com/svn/elements
License: GPLv3 | See LICENSE for the full text
This program is free software: you can redistribute it and/or modify
@@ -22,70 +22,78 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
-along with this program. If not, see <http://www.gnu.org/licenses/>.
+along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
from locals import *
from elements import box2d
+
class CallbackHandler:
- # List of contact callbacks and shapes to start them - sorted by type for quicker access
- # Callbacks are saved as callbacks[callback_type][[function, parameters], ...]
- callbacks = {}
-
+ # List of contact callbacks and shapes to start them - sorted by
+ # type for quicker access Callbacks are saved as
+ # callbacks[callback_type][[function, parameters], ...]
+ callbacks = {}
+
def __init__(self, parent):
self.parent = parent
-
- # init callback dict to avoid those slow try
+
+ # init callback dict to avoid those slow try
# (especially for self.get, as it is called *often*)
for i in xrange(10):
self.callbacks[i] = []
-
+
def add(self, callback_type, callback_handler, *args):
""" Users can add callbacks for certain (or all) collisions
-
+
Parameters:
- callback_type ......... CALLBACK_CONTACT (nothing else for now)
+ callback_type ......... CALLBACK_CONTACT
+ (nothing else for now)
callback_handler ...... a callback function
- args (optional) ....... a list of parameters which can be used with callbacks.get
-
+ args (optional) ....... a list of parameters which can be
+ used with callbacks.get
+
Return:
callback_id ... used to remove a callback later (int)
"""
# Create contact listener if required
- if callback_type in [CALLBACK_CONTACT_ADD, CALLBACK_CONTACT_PERSIST, CALLBACK_CONTACT_REMOVE]:
- if self.parent.listener == None:
+ if callback_type in [CALLBACK_CONTACT_ADD,
+ CALLBACK_CONTACT_PERSIST,
+ CALLBACK_CONTACT_REMOVE]:
+ if self.parent.listener is None:
self.parent.listener = kContactListener(self.get)
- self.parent.world.SetContactListener( self.parent.listener )
+ self.parent.world.SetContactListener(self.parent.listener)
print "* ContactListener added"
-
+
# Get callback dict for this callback_type
c = self.callbacks[callback_type]
-
+
# Append to the Callback Dictionary
c.append([callback_handler, args])
self.callbacks[callback_type] = c
-
+
# Return Callback ID
# ID = callback_type.callback_index (1...n)
return "%i.%i" % (callback_type, len(c))
-
+
def get(self, callback_type):
return self.callbacks[callback_type]
-
+
def start(self, callback_type, *args):
callbacks = self.get(callback_type)
for c in callbacks:
callback, params = c
callback()
+
class kContactListener(box2d.b2ContactListener):
- def __init__(self, get_callbacks):
+
+ def __init__(self, get_callbacks):
# Init the Box2D b2ContactListener
box2d.b2ContactListener.__init__(self)
# Function to get the current callbacks
self.get_callbacks = get_callbacks
-
+
def check_contact(self, contact_type, point):
# Checks if a callback should be started with this contact point
contacts = self.get_callbacks(contact_type)
@@ -94,9 +102,9 @@ class kContactListener(box2d.b2ContactListener):
for c in contacts:
callback, bodylist = c
if len(bodylist) == 0:
- # Without bodylist it's a universal callback (for all bodies)
+ # Without bodylist it's a universal callback (for all bodies)
callback(point)
-
+
else:
# This is a callback with specified bodies
# See if this contact involves one of the specified
@@ -104,19 +112,18 @@ class kContactListener(box2d.b2ContactListener):
b2 = str(point.shape2.GetBody())
for s in bodylist:
s = str(s)
- if b1 == s or b2 == s:
+ if b1 == s or b2 == s:
# Yes, that's the one :)
callback(point)
-
+
def Add(self, point):
"""Called when a contact point is created"""
self.check_contact(CALLBACK_CONTACT_ADD, point)
-
+
def Persist(self, point):
"""Called when a contact point persists for more than a time step"""
self.check_contact(CALLBACK_CONTACT_PERSIST, point)
-
+
def Remove(self, point):
"""Called when a contact point is removed"""
self.check_contact(CALLBACK_CONTACT_REMOVE, point)
-
diff --git a/elements/camera.py b/elements/camera.py
index c45b27d..7fdf975 100644
--- a/elements/camera.py
+++ b/elements/camera.py
@@ -8,7 +8,7 @@ Home: http://elements.linuxuser.at
IRC: #elements on irc.freenode.org
Code: http://www.assembla.com/wiki/show/elements
- svn co http://svn2.assembla.com/svn/elements
+ svn co http://svn2.assembla.com/svn/elements
License: GPLv3 | See LICENSE for the full text
This program is free software: you can redistribute it and/or modify
@@ -22,28 +22,31 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
-along with this program. If not, see <http://www.gnu.org/licenses/>.
+along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
from locals import *
+
class Camera:
""" The Camera class. We will see :)
Please also see: http://www.assembla.com/spaces/elements/tickets/31
-
+
This class currently handles:
- Scaling factor
- Screen Offset from the World Coordinate System
-
+
Inputs from the user have to be checked for them.
- Places to check for it: elements.py, drawing.py, add_objects.py
-
"""
- scale_factor = 1.0 # All coords to the renderer are multiplied with the scale factor in elements.draw()
- track_body = None # Body which means to be tracked. Offset is set at each elements.draw()
-
+ # All coords to the renderer are multiplied with the scale factor
+ # in elements.draw()
+ scale_factor = 1.0
+ # Body which means to be tracked. Offset is set at each elements.draw()
+ track_body = None
+
def __init__(self, parent):
self.parent = parent
-
+
def track(self, body):
""" Start tracking a specific body
"""
@@ -53,17 +56,17 @@ class Camera:
""" Stop tracking a body
"""
self.track_body = None
-
+
def center(self, pos, screenCoord=True, stopTrack=True):
""" Centers the camera at given screen coordinates -- in pixel
Typical call: world.camera.center(event.pos)
-
+
Problem: Works ONLY WITH screenCoord now!
- """
+ """
x, y = pos
x -= self.parent.display_width / 2
- y -= self.parent.display_height / 2
+ y -= self.parent.display_height / 2
if screenCoord:
x /= self.scale_factor
@@ -71,25 +74,30 @@ class Camera:
# Set the offset
self.inc_offset((x, y), screenCoord, stopTrack)
-
+
def set_offset(self, offset, screenCoord=True, stopTrack=True):
- """ Set an offset from the screen to the world cs
+ """ Set an offset from the screen to the world cs
-- in screen (or world) coordinates and in pixel
"""
# Stop tracking of an object
- if stopTrack: self.track_stop()
+ if stopTrack:
+ self.track_stop()
# If screenCoords, we have to bring them to the world cs
- if screenCoord: x, y = self.parent.to_world(offset)
- else: x, y = offset
+ if screenCoord:
+ x, y = self.parent.to_world(offset)
+ else:
+ x, y = offset
self._set_offset((x/self.parent.ppm, y/self.parent.ppm))
def inc_offset(self, offset, screenCoord=True, stopTrack=True):
- """ Increment an offset from the screen to the world cs -- in world coordinates and in pixel
+ """ Increment an offset from the screen to the world cs -- in world
+ coordinates and in pixel
"""
# Stop tracking of an object
- if stopTrack: self.track_stop()
+ if stopTrack:
+ self.track_stop()
# Get Current Offset
x, y = self.parent.screen_offset_pixel
@@ -97,28 +105,30 @@ class Camera:
# Bring the directions into the world coordinate system
if screenCoord:
- if self.parent.inputAxis_x_left: dx *= -1
- if self.parent.inputAxis_y_down: dy *= -1
-
+ if self.parent.inputAxis_x_left:
+ dx *= -1
+ if self.parent.inputAxis_y_down:
+ dy *= -1
+
# Set New Offset
- self._set_offset(((x+dx)/self.parent.ppm, (y+dy)/self.parent.ppm))
-
+ self._set_offset(((x + dx) / self.parent.ppm,
+ (y + dy) / self.parent.ppm))
+
def _set_offset(self, offset):
""" Set the screen offset to the world coordinate system
(using meters and the world coordinate system's orientation)
"""
- x, y = offset
+ x, y = offset
self.parent.screen_offset = (x, y)
- self.parent.screen_offset_pixel = (x*self.parent.ppm, y*self.parent.ppm)
-
+ self.parent.screen_offset_pixel = (x * self.parent.ppm,
+ y * self.parent.ppm)
+
def set_scale_factor(self, factor=1.0):
""" Zoom factor for the renderer 1.0 = 1:1 (original)
"""
self.scale_factor = factor
-
+
def inc_scale_factor(self, factor=0.0):
""" Increases the zooms for the renderer a given factor
"""
self.scale_factor += factor
-
- \ No newline at end of file
diff --git a/elements/drawing.py b/elements/drawing.py
index b4722fb..c79b0f3 100644
--- a/elements/drawing.py
+++ b/elements/drawing.py
@@ -8,7 +8,7 @@ Home: http://elements.linuxuser.at
IRC: #elements on irc.freenode.org
Code: http://www.assembla.com/wiki/show/elements
- svn co http://svn2.assembla.com/svn/elements
+ svn co http://svn2.assembla.com/svn/elements
License: GPLv3 | See LICENSE for the full text
This program is free software: you can redistribute it and/or modify
@@ -22,16 +22,15 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
-along with this program. If not, see <http://www.gnu.org/licenses/>.
+along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
from math import pi
from math import cos
from math import sin
-from math import sqrt
import tools
-# Functions of a rendering class
+# Functions of a rendering class
# mandatory:
# __init__
# start_drawing
@@ -47,32 +46,33 @@ import tools
# for cairo:
# draw_text
# for opengl:
-#
+#
# IMPORTANT
# The drawing functions get the coordinates in their screen coordinate system
# no need for translations anymore :)
+
class draw_pygame(object):
""" This class handles the drawing with pygame, which is really
simple since we only need draw_ellipse and draw_polygon.
"""
lineWidth = 0
-
+
def __init__(self):
""" Load pygame.draw and pygame.Rect, and reference it for
the drawing methods
-
+
Parameters:
surface .... pygame surface (default: None)
- lineWidth ..
+ lineWidth ..
Return: Class draw_pygame()
"""
- print "* Pygame selected as renderer"
+ print "* Pygame selected as renderer"
from pygame import draw
from pygame import Rect
-
+
self.draw = draw
self.Rect = Rect
@@ -93,43 +93,43 @@ class draw_pygame(object):
def start_drawing(self):
pass
-
+
def after_drawing(self):
pass
def draw_circle(self, clr, pt, radius, angle):
""" Draw a circle
-
+
Parameters:
pt ........ (x, y)
clr ....... color in rgb ((r), (g), (b))
- radius .... circle radius
+ radius .... circle radius
angle ..... rotation in radians
-
+
Return: -
"""
x, y = pt
-
+
x1 = x - radius
y1 = y - radius
-
- rect = self.Rect( [x1, y1, 2*radius, 2*radius] )
+
+ rect = self.Rect([x1, y1, 2 * radius, 2 * radius])
self.draw.ellipse(self.surface, clr, rect, self.lineWidth)
-
+
# draw the orientation vector
if radius > 10:
rx = cos(angle) * radius
ry = -sin(angle) * radius
-
- self.draw.line(self.surface, (255,255,255), pt, (x+rx, y+ry))
+
+ self.draw.line(self.surface, (255, 255, 255), pt, (x + rx, y + ry))
def draw_polygon(self, clr, points):
""" Draw a polygon
-
+
Parameters:
clr ....... color in rgb ((r), (g), (b))
points .... polygon points in normal (x,y) positions
-
+
Return: -
"""
self.draw.polygon(self.surface, clr, points, self.lineWidth)
@@ -137,33 +137,34 @@ class draw_pygame(object):
def draw_lines(self, clr, closed, points, width=None):
""" Draw a polygon
-
+
Parameters:
clr ....... color in rgb ((r), (g), (b))
points .... polygon points in normal (x,y) positions
-
+
Return: -
"""
- if width == None:
+ if width is None:
lw = self.lineWidth
- else:
+ else:
lw = width
-
+
self.draw.lines(self.surface, clr, closed, points, lw)
+
class draw_cairo(object):
""" This class handles the drawing with cairo, which is really
simple since we only need draw_ellipse and draw_polygon.
"""
window = None
- da = None
+ da = None
circle_surface = None
- box_surface = None
+ box_surface = None
def __init__(self, drawMethod="filled"):
""" Load cairo.draw and cairo.Rect, and reference it for
the drawing methods
-
+
Return: Class draw_cairo()
"""
print "* Cairo selected as renderer"
@@ -172,12 +173,12 @@ class draw_cairo(object):
self.set_drawing_method(drawMethod)
#self.draw_box = self.draw_box_image
- def set_lineWidth(self, lw): # unused
- self.lineWidth = lw
+ def set_lineWidth(self, lw): # unused
+ self.lineWidth = lw
def set_drawing_area(self, da):
""" Set the area for Cairo to draw to
-
+
da ...... drawing area (gtk.DrawingArea)
Return: -
@@ -193,20 +194,22 @@ class draw_cairo(object):
def start_drawing(self):
self.width, self.height = self.window.get_size()
- self.imagesurface = self.cairo.ImageSurface(self.cairo.FORMAT_ARGB32, self.width, self.height);
+ self.imagesurface = self.cairo.ImageSurface(
+ self.cairo.FORMAT_ARGB32, self.width, self.height)
self.ctx = ctx = self.cairo.Context(self.imagesurface)
- ctx.set_source_rgb(1, 1, 1) # background color
+ ctx.set_source_rgb(1, 1, 1) # background color
ctx.paint()
ctx.move_to(0, 0)
- ctx.set_source_rgb(0, 0, 0) # defaults for the rest of the drawing
+ ctx.set_source_rgb(0, 0, 0) # defaults for the rest of the drawing
ctx.set_line_width(1)
ctx.set_tolerance(0.1)
ctx.set_line_join(self.cairo.LINE_CAP_BUTT)
- # LINE_CAP_BUTT, LINE_CAP_ROUND, LINE_CAP_SQUARE, LINE_JOIN_BEVEL, LINE_JOIN_MITER, LINE_JOIN_ROUND
-
+ # LINE_CAP_BUTT, LINE_CAP_ROUND, LINE_CAP_SQUARE,
+ # LINE_JOIN_BEVEL, LINE_JOIN_MITER, LINE_JOIN_ROUND
+
#ctx.set_dash([20/4.0, 20/4.0], 0)
def after_drawing(self):
@@ -228,14 +231,14 @@ class draw_cairo(object):
clr = tools.rgb2floats(clr)
self.ctx.set_source_rgb(*clr)
self.ctx.move_to(x, y)
- self.ctx.arc(x, y, radius, 0, 2*3.1415)
+ self.ctx.arc(x, y, radius, 0, 2 * pi)
self.ctx.fill()
def draw_circle():
pass
def draw_circle_image(self, clr, pt, radius, angle=0, sf=None):
- if sf == None:
+ if sf is None:
sf = self.circle_surface
x, y = pt
self.ctx.save()
@@ -244,30 +247,32 @@ class draw_cairo(object):
image_r = sf.get_width() / 2
scale = float(radius) / image_r
self.ctx.scale(scale, scale)
- self.ctx.translate(-0.5*sf.get_width(), -0.5*sf.get_height())
+ self.ctx.translate(-0.5 * sf.get_width(), -0.5 * sf.get_height())
self.ctx.set_source_surface(sf)
self.ctx.paint()
self.ctx.restore()
- def draw_image(self, source, pt, scale=1.0, rot=0, sourcepos=(0,0)):
+ def draw_image(self, source, pt, scale=1.0, rot=0, sourcepos=(0, 0)):
self.ctx.save()
self.ctx.rotate(rot)
self.ctx.scale(scale, scale)
destx, desty = self.ctx.device_to_user_distance(pt[0], pt[1])
- self.ctx.set_source_surface(source, destx-sourcepos[0], desty-sourcepos[1])
- self.ctx.rectangle(destx, desty, source.get_width(), source.get_height())
+ self.ctx.set_source_surface(source, destx - sourcepos[0],
+ desty - sourcepos[1])
+ self.ctx.rectangle(destx, desty, source.get_width(),
+ source.get_height())
self.ctx.fill()
self.ctx.restore()
-
+
def draw_polygon(self, clr, points):
""" Draw a polygon
-
+
Parameters:
clr ....... color in rgb ((r), (g), (b))
points .... polygon points in normal (x,y) positions
-
+
Return: -
- """
+ """
clr = tools.rgb2floats(clr)
self.ctx.set_source_rgb(clr[0], clr[1], clr[2])
@@ -275,28 +280,31 @@ class draw_cairo(object):
self.ctx.move_to(pt[0], pt[1])
for pt in points[1:]:
self.ctx.line_to(pt[0], pt[1])
-
+
self.ctx.fill()
-
- def draw_text(self, text, center, clr=(0,0,0), size=12, fontname="Georgia"):
+
+ def draw_text(self, text, center, clr=(0, 0, 0), size=12,
+ fontname="Georgia"):
clr = tools.rgb2floats(clr)
self.ctx.set_source_rgb(clr[0], clr[1], clr[2])
- self.ctx.select_font_face(fontname, self.cairo.FONT_SLANT_NORMAL, self.cairo.FONT_WEIGHT_NORMAL)
+ self.ctx.select_font_face(fontname, self.cairo.FONT_SLANT_NORMAL,
+ self.cairo.FONT_WEIGHT_NORMAL)
self.ctx.set_font_size(size)
x_bearing, y_bearing, width, height = self.ctx.text_extents(text)[:4]
- self.ctx.move_to(center[0] + 0.5 - width / 2 - x_bearing, center[1] + 0.5 - height / 2 - y_bearing)
+ self.ctx.move_to(center[0] + 0.5 - width / 2 - x_bearing,
+ center[1] + 0.5 - height / 2 - y_bearing)
self.ctx.show_text(text)
def draw_lines(self, clr, closed, points):
""" Draw a polygon
-
+
Parameters:
clr ....... color in rgb ((r), (g), (b))
closed .... whether or not to close the lines (as a polygon)
points .... polygon points in normal (x,y) positions
Return: -
- """
+ """
clr = tools.rgb2floats(clr)
self.ctx.set_source_rgb(clr[0], clr[1], clr[2])
@@ -311,16 +319,18 @@ class draw_cairo(object):
self.ctx.stroke()
+
class draw_opengl_pyglet(object):
""" This class handles the drawing with pyglet
"""
lineWidth = 0
+
def __init__(self):
""" Load pyglet.gl, and reference it for the drawing methods
-
+
Parameters:
surface .... not used with pyglet
- lineWidth ..
+ lineWidth ..
"""
print "* OpenGL_Pyglet selected as renderer"
@@ -329,48 +339,49 @@ class draw_opengl_pyglet(object):
def set_lineWidth(self, lw):
self.lineWidth = lw
-
+
def draw_circle(self, clr, pt, radius, a=0):
clr = tools.rgb2floats(clr)
- self.gl.glColor3f(clr[0], clr[1], clr[2])
+ self.gl.glColor3f(clr[0], clr[1], clr[2])
x, y = pt
- segs = 15
- coef = 2.0*pi/segs;
-
- self.gl.glBegin(self.gl.GL_LINE_LOOP)
- for n in range(segs):
- rads = n*coef
- self.gl.glVertex2f(radius*cos(rads + a) + x, radius*sin(rads + a) + y)
- self.gl.glVertex2f(x,y)
- self.gl.glEnd()
+ segs = 15
+ coef = 2.0 * pi / segs
+
+ self.gl.glBegin(self.gl.GL_LINE_LOOP)
+ for n in range(segs):
+ rads = n*coef
+ self.gl.glVertex2f(radius * cos(rads + a) + x,
+ radius * sin(rads + a) + y)
+ self.gl.glVertex2f(x, y)
+ self.gl.glEnd()
def draw_polygon(self, clr, points):
clr = tools.rgb2floats(clr)
- self.gl.glColor3f(clr[0], clr[1], clr[2])
-
+ self.gl.glColor3f(clr[0], clr[1], clr[2])
+
self.gl.glBegin(self.gl.GL_LINES)
p1 = points[0]
for p in points[1:]:
x1, y1 = p1
x2, y2 = p1 = p
-
+
self.gl.glVertex2f(x1, y1)
self.gl.glVertex2f(x2, y2)
x1, y1 = points[0]
-
+
self.gl.glVertex2f(x2, y2)
self.gl.glVertex2f(x1, y1)
self.gl.glEnd()
-
+
def draw_lines(self, clr, closed, points):
pass
def start_drawing(self):
pass
-
+
def after_drawing(self):
pass
diff --git a/elements/elements.py b/elements/elements.py
index 4033688..06d43cc 100644
--- a/elements/elements.py
+++ b/elements/elements.py
@@ -9,7 +9,7 @@ Home: http://elements.linuxuser.at
IRC: #elements on irc.freenode.org
Code: http://www.assembla.com/wiki/show/elements
- svn co http://svn2.assembla.com/svn/elements
+ svn co http://svn2.assembla.com/svn/elements
License: GPLv3 | See LICENSE for the full text
This program is free software: you can redistribute it and/or modify
@@ -23,9 +23,9 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
-along with this program. If not, see <http://www.gnu.org/licenses/>.
+along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
-__version__= '0.11'
+__version__ = '0.11'
__contact__ = '<elements@linuxuser.at>'
# Load Box2D
@@ -34,8 +34,8 @@ try:
except:
print 'Could not load the pybox2d library (Box2D).'
print 'Please run "setup.py install" to install the dependencies.'
- print
- print 'Alternatively, recompile pybox2d for your system and python version.'
+ print
+ print 'Or recompile pybox2d for your system and python version.'
print "See http://code.google.com/p/pybox2d"
exit()
@@ -53,90 +53,97 @@ import callbacks
import camera
# Main Class
+
+
class Elements:
"""The class which handles all interaction with the box2d engine
"""
# Settings
- run_physics =True # Can pause the simulation
- element_count =0 # Element Count
- renderer =None # Drawing class (from drawing.py)
- input =INPUT_PIXELS # Default Input in Pixels! (can change to INPUT_METERS)
- line_width =0 # Line Width in Pixels (0 for fill)
- listener =None
-
- screen_offset = (0, 0) # Offset screen from world coordinate system (x, y) [meter5]
- screen_offset_pixel = (0, 0) # Offset screen from world coordinate system (x, y) [pixel]
-
+ run_physics = True # Can pause the simulation
+ element_count = 0 # Element Count
+ renderer = None # Drawing class (from drawing.py)
+ # Default Input in Pixels! (can change to INPUT_METERS)
+ input_unit = INPUT_PIXELS
+ line_width = 0 # Line Width in Pixels (0 for fill)
+ listener = None
+
+ # Offset screen from world coordinate system (x, y) [meter5]
+ screen_offset = (0, 0)
+ # Offset screen from world coordinate system (x, y) [pixel]
+ screen_offset_pixel = (0, 0)
+
# The internal coordination system is y+=up, x+=right
# But it's possible to change the input coords to something else,
# they will then be translated on input
- inputAxis_x_left = False # positive to the right by default
- inputAxis_y_down = True # positive to up by default
+ inputAxis_x_left = False # positive to the right by default
+ inputAxis_y_down = True # positive to up by default
mouseJoint = None
- def __init__(self, screen_size, gravity=(0.0,-9.0), ppm=100.0, renderer='pygame'):
+ def __init__(self, screen_size, gravity=(0.0, -9.0), ppm=100.0,
+ renderer='pygame'):
""" Init the world with boundaries and gravity, and init colors.
-
+
Parameters:
screen_size .. (w, h) -- screen size in pixels [int]
gravity ...... (x, y) in m/s^2 [float] default: (0.0, -9.0)
ppm .......... pixels per meter [float] default: 100.0
- renderer ..... which drawing method to use (str) default: 'pygame'
+ renderer ..... which drawing method to use (str) default:
+ 'pygame'
Return: class Elements()
"""
self.set_screenSize(screen_size)
self.set_drawingMethod(renderer)
-
+
# Create Subclasses
self.add = add_objects.Add(self)
self.callbacks = callbacks.CallbackHandler(self)
self.camera = camera.Camera(self)
-
+
# Set Boundaries
- self.worldAABB=box2d.b2AABB()
+ self.worldAABB = box2d.b2AABB()
self.worldAABB.lowerBound = (-100.0, -100.0)
self.worldAABB.upperBound = (100.0, 100.0)
-
+
# Gravity + Bodies will sleep on outside
self.gravity = gravity
self.doSleep = True
-
+
# Create the World
self.world = box2d.b2World(self.worldAABB, self.gravity, self.doSleep)
- # Init Colors
+ # Init Colors
self.init_colors()
-
+
# Set Pixels per Meter
self.ppm = ppm
- def set_inputUnit(self, input):
+ def set_inputUnit(self, input_unit):
""" Change the input unit to either meter or pixels
-
+
Parameters:
input ... INPUT_METERS or INPUT_PIXELS
-
+
Return: -
"""
- self.input = input
-
+ self.input_unit = input_unit
+
def set_inputAxisOrigin(self, left=True, top=False):
""" Change the origin of the input coordinate system axis
-
+
Parameters:
left ... True or False -- x = 0 is at the left?
top .... True or False -- y = 0 is at the top?
-
+
Return: -
- """
+ """
self.inputAxis_x_left = not left
self.inputAxis_y_down = top
def set_drawingMethod(self, m, *kw):
""" Set a drawing method (from drawing.py)
-
+
Parameters:
m .... 'pygame' or 'cairo'
*kw .. keywords to pass to the initializer of the drawing method
@@ -144,86 +151,89 @@ class Elements:
Return: True if ok, False if no method identifier m found
"""
try:
- self.renderer = getattr(drawing, "draw_%s" % m) (*kw)
+ self.renderer = getattr(drawing, "draw_%s" % m)(*kw)
return True
except AttributeError:
return False
-
+
def set_screenSize(self, size):
""" Set the current screen size
-
- Parameters:
+
+ Parameters:
size ... (int(width), int(height)) in pixels
-
+
Return: -
"""
self.display_width, self.display_height = size
def init_colors(self):
""" Init self.colors with a fix set of hex colors
-
- Return: -
+
+ Return: -
"""
self.fixed_color = None
self.cur_color = 0
self.colors = [
- "#737934", "#729a55", "#040404", "#1d4e29", "#ae5004", "#615c57",
- "#6795ce", "#203d61", "#8f932b"
+ "#737934", "#729a55", "#040404", "#1d4e29", "#ae5004", "#615c57",
+ "#6795ce", "#203d61", "#8f932b"
]
shuffle(self.colors)
def set_color(self, clr):
- """ Set a fixed color for all future Elements (until reset_color() is called)
-
- Parameters:
+ """ Set a fixed color for all future Elements (until reset_color()
+ is called)
+
+ Parameters:
clr ... Hex '#123123' or RGB ((r), (g), (b))
-
+
Return: -
"""
self.fixed_color = clr
-
+
def reset_color(self):
""" All Elements from now on will be drawn in random colors
-
- Return: -
+
+ Return: -
"""
self.fixed_color = None
def get_color(self):
- """ Get a color - either the fixed one or the next from self.colors
-
- Return: clr = ((R), (G), (B))
+ """ Get a color - either the fixed one or the next from self.colors
+
+ Return: clr = ((R), (G), (B))
"""
- if self.fixed_color != None:
+ if self.fixed_color is not None:
return self.fixed_color
-
- if self.cur_color == len(self.colors):
+
+ if self.cur_color == len(self.colors):
self.cur_color = 0
shuffle(self.colors)
-
+
clr = self.colors[self.cur_color]
if clr[0] == "#":
clr = tools.hex2rgb(clr)
-
+
self.cur_color += 1
return clr
-
+
def update(self, fps=50.0, vel_iterations=10, pos_iterations=8):
""" Update the physics, if not paused (self.run_physics)
-
+
Parameters:
fps ............. fps with which the physics engine shall work
- vel_iterations .. velocity substeps per step for smoother simulation
- pos_iterations .. position substeps per step for smoother simulation
-
+ vel_iterations .. velocity substeps per step for smoother
+ simulation
+ pos_iterations .. position substeps per step for smoother
+ simulation
+
Return: -
"""
if self.run_physics:
self.world.Step(1.0 / fps, vel_iterations, pos_iterations)
def translate_coord(self, point):
- """ Flips the coordinates in another coordinate system orientation, if necessary
- (screen <> world coordinate system)
+ """ Flips the coordinates in another coordinate system orientation,
+ if necessary (screen <> world coordinate system)
"""
x, y = point
@@ -232,46 +242,49 @@ class Elements:
if self.inputAxis_y_down:
y = self.display_height - y
-
+
return (x, y)
-
+
def translate_coords(self, pointlist):
- """ Flips the coordinates in another coordinate system orientation, if necessary
- (screen <> world coordinate system)
- """
- p_out = []
+ """Flips the coordinates in another coordinate system orientation, if
+ necessary (screen <> world coordinate system)
+ """
+ p_out = []
for p in pointlist:
p_out.append(self.translate_coord(p))
return p_out
def to_world(self, pos):
- """ Transfers a coordinate from the screen to the world coordinate system (pixels)
+ """ Transfers a coordinate from the screen to the world
+ coordinate system (pixels)
+
- Change to the right axis orientation
- Include the offset: screen -- world coordinate system
- - Include the scale factor (Screen coordinate system might have a scale factor)
+ - Include the scale factor (Screen coordinate system might have
+ a scale factor)
"""
dx, dy = self.screen_offset_pixel
-
+
x = pos[0] / self.camera.scale_factor
y = pos[1] / self.camera.scale_factor
-
+
x, y = self.translate_coord((round(x), round(y)))
- return (x+dx, y+dy)
-
+ return(x + dx, y + dy)
+
def to_screen(self, pos):
- """ Transfers a coordinate from the world to the screen coordinate system (pixels)
- and by the screen offset
+ """Transfers a coordinate from the world to the screen coordinate
+ system (pixels) and by the screen offset
"""
dx, dy = self.screen_offset_pixel
x = pos[0] - dx
y = pos[1] - dy
-
+
sx, sy = self.translate_coord((x, y))
return (sx * self.camera.scale_factor, sy * self.camera.scale_factor)
-
+
def meter_to_screen(self, i):
return i * self.ppm * self.camera.scale_factor
-
+
def get_bodies_at_pos(self, search_point, include_static=False, area=0.01):
""" Check if given point (screen coordinates) is inside any body.
If yes, return all found bodies, if not found return False
@@ -280,11 +293,11 @@ class Elements:
sx /= self.ppm
sy /= self.ppm
- f = area/self.camera.scale_factor
+ f = area / self.camera.scale_factor
- AABB=box2d.b2AABB()
- AABB.lowerBound = (sx-f, sy-f)
- AABB.upperBound = (sx+f, sy+f)
+ AABB = box2d.b2AABB()
+ AABB.lowerBound = (sx - f, sy - f)
+ AABB.upperBound = (sx + f, sy + f)
amount, shapes = self.world.Query(AABB, 2)
@@ -297,101 +310,110 @@ class Elements:
if not include_static:
if body.IsStatic() or body.GetMass() == 0.0:
continue
-
+
if s.TestPoint(body.GetXForm(), (sx, sy)):
bodylist.append(body)
return bodylist
-
+
def draw(self):
""" If a drawing method is specified, this function passes the objects
to the module in pixels.
-
+
Return: True if the objects were successfully drawn
False if the renderer was not set or another error occurred
"""
self.callbacks.start(CALLBACK_DRAWING_START)
-
- # No need to run through the loop if there's no way to draw
- if not self.renderer:
+
+ # No need to run through the loop if there's no way to draw
+ if not self.renderer:
return False
if self.camera.track_body:
# Get Body Center
- p1 = self.camera.track_body.GetWorldCenter()
-
+ p1 = self.camera.track_body.GetWorldCenter()
+
# Center the Camera There, False = Don't stop the tracking
- self.camera.center(self.to_screen((p1.x*self.ppm, p1.y*self.ppm)), stopTrack=False)
-
+ self.camera.center(self.to_screen((p1.x * self.ppm,
+ p1.y * self.ppm)),
+ stopTrack=False)
+
# Walk through all known elements
self.renderer.start_drawing()
-
+
for body in self.world.bodyList:
xform = body.GetXForm()
shape = body.GetShapeList()
angle = body.GetAngle()
-
+
if shape:
userdata = body.GetUserData()
- clr = userdata['color']
-
- for shape in body.shapeList:
+ if 'color' in userdata:
+ clr = userdata['color']
+ else:
+ clr = self.colors[0]
+
+ for shape in body.shapeList:
type = shape.GetType()
-
+
if type == box2d.e_circleShape:
position = box2d.b2Mul(xform, shape.GetLocalPosition())
-
- pos = self.to_screen((position.x*self.ppm, position.y*self.ppm))
- self.renderer.draw_circle(clr, pos, self.meter_to_screen(shape.radius), angle)
+
+ pos = self.to_screen((position.x * self.ppm,
+ position.y * self.ppm))
+
+ self.renderer.draw_circle(
+ clr, pos, self.meter_to_screen(shape.radius), angle)
elif type == box2d.e_polygonShape:
points = []
for v in shape.vertices:
pt = box2d.b2Mul(xform, v)
- x, y = self.to_screen((pt.x*self.ppm, pt.y*self.ppm))
+ x, y = self.to_screen((pt.x * self.ppm,
+ pt.y * self.ppm))
points.append([x, y])
self.renderer.draw_polygon(clr, points)
-
+
else:
- print " unknown shape type:%d" % shape.GetType()
-
+ print "unknown shape type:%d" % shape.GetType()
for joint in self.world.jointList:
p2 = joint.GetAnchor1()
- p2 = self.to_screen((p2.x*self.ppm, p2.y*self.ppm))
-
+ p2 = self.to_screen((p2.x * self.ppm, p2.y * self.ppm))
+
p1 = joint.GetAnchor2()
- p1 = self.to_screen((p1.x*self.ppm, p1.y*self.ppm))
-
+ p1 = self.to_screen((p1.x * self.ppm, p1.y * self.ppm))
+
if p1 == p2:
- self.renderer.draw_circle((255,255,255), p1, 2, 0)
+ self.renderer.draw_circle((255, 255, 255), p1, 2, 0)
else:
- self.renderer.draw_lines((0,0,0), False, [p1, p2], 3)
+ self.renderer.draw_lines((0, 0, 0), False, [p1, p2], 3)
self.callbacks.start(CALLBACK_DRAWING_END)
self.renderer.after_drawing()
-
- return True
+ return True
def mouse_move(self, pos):
pos = self.to_world(pos)
x, y = pos
x /= self.ppm
y /= self.ppm
-
+
if self.mouseJoint:
- self.mouseJoint.SetTarget((x,y))
+ self.mouseJoint.SetTarget((x, y))
def pickle_save(self, fn, additional_vars={}):
import cPickle as pickle
self.add.remove_mouseJoint()
-
+
if not additional_vars and hasattr(self, '_pickle_vars'):
- additional_vars=dict((var, getattr(self, var)) for var in self._pickle_vars)
+ additional_vars = dict((var, getattr(self, var))
+ for var in self._pickle_vars)
- save_values = [self.world, box2d.pickle_fix(self.world, additional_vars, 'save')]
+ save_values = [self.world, box2d.pickle_fix(self.world,
+ additional_vars, 'save')]
try:
pickle.dump(save_values, open(fn, 'wb'))
@@ -411,13 +433,13 @@ class Elements:
try:
world, variables = pickle.load(open(fn, 'rb'))
world = world._pickle_finalize()
- variables = box2d.pickle_fix(world, variables, 'load')
+ variables = box2d.pickle_fix(world, variables, 'load')
except Exception, s:
print 'Error while loading world: ', s
return
-
+
self.world = world
-
+
if set_vars:
# reset the additional saved variables:
for var, value in variables.items():
@@ -461,7 +483,8 @@ class Elements:
if shapename == "b2CircleShape":
modelshape['type'] = 'circle'
modelshape['radius'] = shape.radius
- modelshape['localPosition'] = shape.localPosition.tuple()
+ modelshape['localPosition'] = \
+ shape.localPosition.tuple()
if shapename == "b2PolygonShape":
modelshape['type'] = 'polygon'
modelshape['vertices'] = shape.vertices
@@ -528,19 +551,19 @@ class Elements:
def json_load(self, path, serialized=False):
import json
- self.world.GetGroundBody().userData = {"saveid" : 0}
+ self.world.GetGroundBody().userData = {"saveid": 0}
f = open(path, 'r')
worldmodel = json.loads(f.read())
f.close()
- #clean world
+ # clean world
for joint in self.world.GetJointList():
self.world.DestroyJoint(joint)
for body in self.world.GetBodyList():
if body != self.world.GetGroundBody():
self.world.DestroyBody(body)
- #load bodys
+ # load bodies
for body in worldmodel['bodylist']:
bodyDef = box2d.b2BodyDef()
bodyDef.position = body['position']
@@ -550,7 +573,7 @@ class Elements:
#_logger.debug(newBody)
newBody.angularVelocity = body['angularVelocity']
newBody.linearVelocity = body['linearVelocity']
- if body.has_key('shapes'):
+ if 'shapes' in body:
for shape in body['shapes']:
if shape['type'] == 'polygon':
polyDef = box2d.b2PolygonDef()
@@ -565,7 +588,7 @@ class Elements:
circleDef.density = shape['density']
circleDef.restitution = shape['restitution']
circleDef.friction = shape['friction']
- circleDef.localPosition = shape['localPosition']
+ circleDef.localPosition = shape['localPosition']
newBody.CreateShape(circleDef)
newBody.SetMassFromShapes()
@@ -577,7 +600,7 @@ class Elements:
body2 = self.getBodyWithSaveId(joint['body2'])
anch2 = joint['anchor2']
jointDef.collideConnected = joint['collideConnected']
- jointDef.Initialize(body1,body2,anch1,anch2)
+ jointDef.Initialize(body1, body2, anch1, anch2)
jointDef.SetUserData(joint['userData'])
self.world.CreateJoint(jointDef)
if joint['type'] == 'revolute':
@@ -585,7 +608,7 @@ class Elements:
body1 = self.getBodyWithSaveId(joint['body1'])
body2 = self.getBodyWithSaveId(joint['body2'])
anchor = joint['anchor']
- jointDef.Initialize(body1,body2,anchor)
+ jointDef.Initialize(body1, body2, anchor)
jointDef.SetUserData(joint['userData'])
jointDef.enableMotor = joint['enableMotor']
jointDef.motorSpeed = joint['motorSpeed']
@@ -594,17 +617,17 @@ class Elements:
self.additional_vars = {}
addvars = {}
- for (k,v) in worldmodel['additional_vars'].items():
+ for (k, v) in worldmodel['additional_vars'].items():
addvars[k] = v
if serialized and 'trackinfo' in addvars:
trackinfo = addvars['trackinfo']
for key, info in trackinfo.iteritems():
if not info[3]:
- addvars['trackinfo'][key][0] = \
- self.getBodyWithSaveId(info[0])
- addvars['trackinfo'][key][1] = \
- self.getBodyWithSaveId(info[1])
+ addvars['trackinfo'][key][0] = \
+ self.getBodyWithSaveId(info[0])
+ addvars['trackinfo'][key][1] = \
+ self.getBodyWithSaveId(info[1])
else:
addvars['trackinfo'][key][0] = None
addvars['trackinfo'][key][1] = None
@@ -612,9 +635,9 @@ class Elements:
self.additional_vars = addvars
for body in self.world.GetBodyList():
- del body.userData['saveid'] #remove temporary data
+ del body.userData['saveid'] # remove temporary data
- def getBodyWithSaveId(self,saveid):
+ def getBodyWithSaveId(self, saveid):
for body in self.world.GetBodyList():
if body.userData['saveid'] == saveid:
return body
diff --git a/elements/locals.py b/elements/locals.py
index 85528f7..352518a 100644
--- a/elements/locals.py
+++ b/elements/locals.py
@@ -8,7 +8,7 @@ Home: http://elements.linuxuser.at
IRC: #elements on irc.freenode.org
Code: http://www.assembla.com/wiki/show/elements
- svn co http://svn2.assembla.com/svn/elements
+ svn co http://svn2.assembla.com/svn/elements
License: GPLv3 | See LICENSE for the full text
This program is free software: you can redistribute it and/or modify
@@ -22,16 +22,16 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
-along with this program. If not, see <http://www.gnu.org/licenses/>.
+along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
INPUT_METERS = 0
INPUT_PIXELS = 1
CALLBACK_CONTACT_ADD = 0
CALLBACK_CONTACT_PERSIST = 1
-CALLBACK_CONTACT_REMOVE = 2
+CALLBACK_CONTACT_REMOVE = 2
-CALLBACK_DRAWING_START = 3
-CALLBACK_DRAWING_END = 4
+CALLBACK_DRAWING_START = 3
+CALLBACK_DRAWING_END = 4
FLT_EPSILON = 1.192092896e-07
diff --git a/elements/menu.py b/elements/menu.py
index 1c8a417..3ea5d64 100644
--- a/elements/menu.py
+++ b/elements/menu.py
@@ -8,7 +8,7 @@ Home: http://elements.linuxuser.at
IRC: #elements on irc.freenode.org
Code: http://www.assembla.com/wiki/show/elements
- svn co http://svn2.assembla.com/svn/elements
+ svn co http://svn2.assembla.com/svn/elements
License: GPLv3 | See LICENSE for the full text
This program is free software: you can redistribute it and/or modify
@@ -22,7 +22,7 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
-along with this program. If not, see <http://www.gnu.org/licenses/>.
+along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
import pygame
from pygame.locals import *
@@ -32,24 +32,25 @@ import tools
COLOR_HEX_BLUE1 = "6491a4"
COLOR_HEX_BLUE2 = "9ec9ff"
-class MenuItem:
+
+class MenuItem:
# padding [px]: left, top, right, bottom
padding = (5, 2, 5, 2)
-
+
def empty(self, *args):
pass
-
+
def __init__(self, title, pos, userData, parent=None, callback=None):
self.title = title
self.userData = userData
self.parent = parent
self.childs = []
-
+
if self.parent:
self.visible = False
else:
self.visible = True
-
+
if callback:
self.callback = callback
else:
@@ -57,143 +58,150 @@ class MenuItem:
# Create Surface and Stuff :)
self.font = pygame.font.Font(None, 32)
- text = self.font.render(title, 1, (255,255,255))
+ text = self.font.render(title, 1, (255, 255, 255))
- rx, ry, rw, rh = rect = text.get_rect()
+ rx, ry, rw, rh = text.get_rect()
pl, pt, pr, pb = self.padding
-
- s1 = pygame.Surface((rw+pl+pr, rh+pt+pb))
+
+ s1 = pygame.Surface((rw + pl + pr, rh + pt + pb))
s1.fill(tools.hex2rgb(COLOR_HEX_BLUE1))
s1.blit(text, (pl, pt))
- s2 = pygame.Surface((rw+pl+pr, rh+pt+pb))
+ s2 = pygame.Surface((rw + pl + pr, rh + pt + pb))
s2.fill(tools.hex2rgb(COLOR_HEX_BLUE2))
s2.blit(text, (pl, pt))
-
+
self.rect = s1.get_rect().move(pos)
self.surface_inactive = s1
self.surface_active = s2
-
+
def pos_inside(self, pos):
if not self.visible:
return False
-
- x,y,w,h = self.rect
+
+ x, y, w, h = self.rect
px, py = pos
-
- if px > x and px < x+w and py > y and py < y+h:
+
+ if px > x and px < x + w and py > y and py < y + h:
return True
else:
return False
-
+
+
class MenuClass:
""" Important: Never delete an Item, just overwrite it if deleting,
else the menuitem id's get messed up
"""
# current active menu point it
focus = False
-
+
# each item is stored as MenuItem
- items = []
+ items = []
# where to start drawing
start_at = (0, 0)
-
+
# menubar properties
- height = 0 # px
- width = 0 # px (set in set_width)
- setWidth = False # if width was set by hand (if not, increase width by adding stuff)
-
+ height = 0 # px
+ width = 0 # px (set in set_width)
+ # if width was set by hand (if not, increase width by adding stuff)
+ setWidth = False
+
def __init__(self):
self.draw_at = self.start_at
-
+
def set_width(self, width):
self.setWidth = True
self.width = width
-
+
def addItem(self, title, callback=None, userData='', parent=None):
# Get position for the Item
- if parent: draw_at = (0,0)
- else: draw_at = self.draw_at
-
+ if parent:
+ draw_at = (0, 0)
+ else:
+ draw_at = self.draw_at
+
# Create Items
- M = MenuItem(title=title, pos=draw_at, userData=userData, parent=parent, callback=callback)
+ M = MenuItem(title=title, pos=draw_at, userData=userData,
+ parent=parent, callback=callback)
self.items.append(M)
-
+
# Set a new position
- x,y,w,h = M.rect
+ x, y, w, h = M.rect
x, y = self.draw_at
-
+
if parent:
# Set the info that the item has a child to the parent item
- self.items[parent-1].childs.append(len(self.items)-1)
+ self.items[parent-1].childs.append(len(self.items) - 1)
else:
# New next drawing position
- self.draw_at = (x+w, y)
+ self.draw_at = (x + w, y)
# Adjust the width of the menu bar
if not self.setWidth:
- self.width = x+w
+ self.width = x + w
# Adjust the height of the menu bar
- if h > self.height: self.height = h + 2
+ if h > self.height:
+ self.height = h + 2
# Return array id of this item
return len(self.items)
-
+
def click(self, pos):
""" Checks a click for menuitems and starts the callback if found
-
- Return: True if a menu item was found or hit the MenuBar, and False if not
+
+ Return: True if a menu item was found or hit the MenuBar,
+ and False if not
"""
focus_in = self.focus
-
+
found = False
for i in xrange(len(self.items)):
item = self.items[i]
if item.pos_inside(pos):
found = True
item.callback(item.title, item.userData)
-
+
# Expand the menu if necessary
if len(item.childs) > 0:
- self.focus = i+1
-
- # Close any opened menu windows if clicked somewhere else
+ self.focus = i + 1
+
+ # Close any opened menu windows if clicked somewhere else
if self.focus == focus_in:
self.focus = False
- self.subwin_rect = (0,0,0,0)
+ self.subwin_rect = (0, 0, 0, 0)
for item in self.items:
if item.parent:
item.visible = False
-
+
# Check if click is inside menubar
- x,y = pos
+ x, y = pos
mx, my = self.start_at
-
+
if found:
return True
- else:
+ else:
return False
-
+
def draw(self, surface):
""" Draw the menu with pygame on a given surface
"""
s = pygame.Surface((self.width, self.height))
s.fill(tools.hex2rgb(COLOR_HEX_BLUE1))
- surface.blit(s, (0,0))
-
+ surface.blit(s, (0, 0))
+
for i in xrange(len(self.items)):
item = self.items[i]
- if not item.parent:
- x,y,w,h = item.rect
- if self.focus == i+1:
- surface.blit(item.surface_active, (x,y))
+ if not item.parent:
+ x, y, w, h = item.rect
+ if self.focus == i + 1:
+ surface.blit(item.surface_active, (x, y))
else:
- surface.blit(item.surface_inactive, (x,y))
+ surface.blit(item.surface_inactive, (x, y))
# If a menu item is open, draw that
if self.focus:
@@ -205,33 +213,33 @@ class MenuClass:
if j.parent == self.focus:
i.append(j)
x, y, w, h = j.rect
- if w > width: width = w
+ if w > width:
+ width = w
height += h
if len(i) > 0:
s = pygame.Surface((width, height))
s.fill(tools.hex2rgb(COLOR_HEX_BLUE1))
- # Parent Coordinates
- px, py, pw, ph = self.items[self.focus-1].rect
-
+ # Parent Coordinates
+ px, py, pw, ph = self.items[self.focus - 1].rect
+
# y Counter
y = 0
-
+
for item in i:
item.visible = True
s.blit(item.surface_inactive, (0, y))
- ix, iy, iw, ih = item.rect
+ ix, iy, iw, ih = item.rect
if (ix, iy) == (0, 0):
- item.rect = item.rect.move((px, y+ph))
- ix, iy, iw, ih = item.rect
+ item.rect = item.rect.move((px, y + ph))
+ ix, iy, iw, ih = item.rect
if iw < width:
- item.rect = (ix,iy,width,ih)
+ item.rect = (ix, iy, width, ih)
y += ih
-
- surface.blit(s, (px,py+ph))
- self.subwin_rect = s.get_rect().move(px, py+ph)
+ surface.blit(s, (px, py + ph))
+ self.subwin_rect = s.get_rect().move(px, py + ph)
diff --git a/elements/tools.py b/elements/tools.py
index 9851ff8..3c81eb2 100644
--- a/elements/tools.py
+++ b/elements/tools.py
@@ -8,7 +8,7 @@ Home: http://elements.linuxuser.at
IRC: #elements on irc.freenode.org
Code: http://www.assembla.com/wiki/show/elements
- svn co http://svn2.assembla.com/svn/elements
+ svn co http://svn2.assembla.com/svn/elements
License: GPLv3 | See LICENSE for the full text
This program is free software: you can redistribute it and/or modify
@@ -22,19 +22,24 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
-along with this program. If not, see <http://www.gnu.org/licenses/>.
+along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
# Some Hex Tools
+
+
def hex2dec(hex):
""" Convert and hex value in a decimal number
- """
+ """
return int(hex, 16)
-def hex2rgb(hex):
+
+def hex2rgb(hex):
""" Convert a hex color (#123abc) in RGB ((r), (g), (b))
"""
- if hex[0:1] == '#': hex = hex[1:];
- return (hex2dec(hex[:2]), hex2dec(hex[2:4]), hex2dec(hex[4:6]))
+ if hex[0: 1] == '#':
+ hex = hex[1:]
+ return (hex2dec(hex[:2]), hex2dec(hex[2: 4]), hex2dec(hex[4: 6]))
+
def rgb2floats(rgb):
"""Convert a color in the RGB (0..255,0..255,0..255) format to the
@@ -45,21 +50,21 @@ def rgb2floats(rgb):
ret.append(float(c) / 255)
return ret
+
def point_in_poly(point, poly):
#print ">", point, poly
x, y = point
n = len(poly)
inside = False
- p1x,p1y = poly[0]
- for i in range(n+1):
- p2x,p2y = poly[i % n]
- if y > min(p1y,p2y):
- if y <= max(p1y,p2y):
- if x <= max(p1x,p2x):
+ p1x, p1y = poly[0]
+ for i in range(n + 1):
+ p2x, p2y = poly[i % n]
+ if y > min(p1y, p2y):
+ if y <= max(p1y, p2y):
+ if x <= max(p1x, p2x):
if p1y != p2y:
- xinters = (y-p1y)*(p2x-p1x)/(p2y-p1y)+p1x
+ xinters = (y - p1y) * (p2x - p1x) / (p2y - p1y) + p1x
if p1x == p2x or x <= xinters:
inside = not inside
- p1x,p1y = p2x,p2y
+ p1x, p1y = p2x, p2y
return inside
- \ No newline at end of file
diff --git a/elements/tools_poly.py b/elements/tools_poly.py
index 5ca3986..a2bb425 100644
--- a/elements/tools_poly.py
+++ b/elements/tools_poly.py
@@ -8,7 +8,7 @@ Home: http://elements.linuxuser.at
IRC: #elements on irc.freenode.org
Code: http://www.assembla.com/wiki/show/elements
- svn co http://svn2.assembla.com/svn/elements
+ svn co http://svn2.assembla.com/svn/elements
License: GPLv3 | See LICENSE for the full text
This program is free software: you can redistribute it and/or modify
@@ -22,7 +22,7 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
-along with this program. If not, see <http://www.gnu.org/licenses/>.
+along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
from functools import partial
@@ -33,167 +33,177 @@ from math import degrees
from math import acos
from locals import *
-from elements import box2d
+
def calc_center(points):
""" Calculate the center of a polygon
-
+
Return: The center (x,y)
"""
- tot_x, tot_y = 0,0
+ tot_x, tot_y = 0, 0
for p in points:
tot_x += p[0]
tot_y += p[1]
n = len(points)
- return (tot_x/n, tot_y/n)
-
+ return (tot_x / n, tot_y / n)
+
+
def poly_center_vertices(pointlist):
""" Rearranges vectors around the center
-
+
Return: pointlist ([(x, y), ...])
- """
+ """
poly_points_center = []
- center = cx, cy = calc_center(pointlist)
-
+ cx, cy = calc_center(pointlist)
+
for p in pointlist:
x = p[0] - cx
y = cy - p[1]
poly_points_center.append((x, y))
-
+
return poly_points_center
-
+
+
def is_line(vertices, tolerance=25.0):
""" Check if passed vertices are a line. Done by comparing
the angles of all vectors and check tolerance.
-
+
Parameters:
vertices ... a list of vertices (x, y)
tolerance .. how many degrees should be allowed max to be a line
-
+
Returns: True if line, False if no line
"""
- if len(vertices) <= 2:
+ if len(vertices) <= 2:
return True
# Step 1: Points -> Vectors
p_old = vertices[0]
alphas = []
-
for p in vertices[1:]:
x1, y1 = p_old
x2, y2 = p
p_old = p
- # Create Vector
- vx, vy = (x2-x1, y2-y1)
-
+ # Create Vector
+ vx, vy = (x2 - x1, y2 - y1)
+
# Check Length
- l = sqrt((vx*vx) + (vy*vy))
- if l == 0.0: continue
+ l = sqrt((vx * vx) + (vy * vy))
+ if l == 0.0:
+ continue
# Normalize vector
vx /= l
vy /= l
-
+
# Append angle
- if fabs(vx) < 0.2: alpha = 90.0
- else: alpha = degrees(atan2(vy,vx))
+ if fabs(vx) < 0.2:
+ alpha = 90.0
+ else:
+ alpha = degrees(atan2(vy, vx))
alphas.append(fabs(alpha))
-
+
# Sort angles
alphas.sort()
-
+
# Get maximum difference
alpha_diff = fabs(alphas[-1] - alphas[0])
print "alpha difference:", alpha_diff
-
+
if alpha_diff < tolerance:
return True
else:
return False
-
+
+
def reduce_poly_by_angle(vertices, tolerance=10.0, minlen=20):
- """ This function reduces a poly by the angles of the vectors (detect lines)
- If the angle difference from one vector to the last > tolerance: use last point
- If the angle is quite the same, it's on the line
-
+ """ This function reduces a poly by the angles of the vectors
+ (detect lines).
+ If the angle difference from one vector to the last > tolerance:
+ use last point.
+ If the angle is quite the same, it's on the line.
+
Parameters:
vertices ... a list of vertices (x, y)
tolerance .. how many degrees should be allowed max
-
+
Returns: (1) New Pointlist, (2) Soft reduced pointlist (reduce_poly())
"""
v_last = vertices[-1]
vertices = vxx = reduce_poly(vertices, minlen)
-
- p_new = []
+
+ p_new = []
p_new.append(vertices[0])
-
- dir = None
+
+ dir = None
is_convex = True
-
- for i in xrange(len(vertices)-1):
+
+ for i in xrange(len(vertices) - 1):
if i == 0:
p_old = vertices[i]
continue
-
+
x1, y1 = p_old
x2, y2 = vertices[i]
- x3, y3 = vertices[i+1]
+ x3, y3 = vertices[i + 1]
p_old = vertices[i]
-
+
# Create Vectors
v1x = (x2 - x1) * 1.0
v1y = (y2 - y1) * 1.0
-
+
v2x = (x3 - x2) * 1.0
v2y = (y3 - y2) * 1.0
# Calculate angle
a = ((v1x * v2x) + (v1y * v2y))
- b = sqrt((v1x*v1x) + (v1y*v1y))
- c = sqrt((v2x*v2x) + (v2y*v2y))
+ b = sqrt((v1x * v1x) + (v1y * v1y))
+ c = sqrt((v2x * v2x) + (v2y * v2y))
+
+ # No Division by 0 :)
+ if (b * c) == 0.0:
+ continue
- # No Division by 0 :)
- if (b*c) == 0.0: continue
-
# Get the current degrees
# We have a bug here sometimes...
try:
- angle = degrees(acos(a / (b*c)))
+ angle = degrees(acos(a / (b * c)))
except:
# cos=1.0
- print "cos=", a/(b*c)
+ print "cos=", a/(b * c)
continue
-
+
# Check if inside tolerance
if fabs(angle) > tolerance:
- p_new.append(vertices[i])
- # print "x", 180-angle, is_left(vertices[i-1], vertices[i], vertices[i+1])
-
+ p_new.append(vertices[i])
+ # print "x", 180-angle, is_left(vertices[i-1],
+ # vertices[i], vertices[i+1])
+
# Check if convex:
- if dir == None:
- dir = is_left(vertices[i-1], vertices[i], vertices[i+1])
+ if dir is None:
+ dir = is_left(vertices[i - 1], vertices[i], vertices[i + 1])
else:
- if dir != is_left(vertices[i-1], vertices[i], vertices[i+1]):
+ if dir != is_left(vertices[i - 1], vertices[i],
+ vertices[i + 1]):
is_convex = False
-
+
# We also want to append the last point :)
p_new.append(v_last)
-
+
# Returns: (1) New Pointlist, (2) Soft reduced pointlist (reduce_poly())
return p_new, is_convex
- """ OLD FUNCTION: """
+ """ OLD FUNCTION: """
# Wipe all points too close to each other
vxx = vertices = reduce_poly(vertices, minlen)
-
+
# Create Output List
p_new = []
p_new.append(vertices[0])
-
+
# Set the starting vertice
p_old = vertices[0]
alpha_old = None
@@ -205,110 +215,121 @@ def reduce_poly_by_angle(vertices, tolerance=10.0, minlen=20):
p_old = (x2, y2)
# Make Vector
- vx, vy = (x2-x1, y2-y1)
-
+ vx, vy = (x2 - x1, y2 - y1)
+
# Vector length
- l = sqrt((vx*vx) + (vy*vy))
-
+ l = sqrt((vx * vx) + (vy * vy))
+
# normalize
vx /= l
vy /= l
-
+
# Get Angle
if fabs(vx) < 0.2:
alpha = 90
- else:
- alpha = degrees(atan2(vy,vx))
+ else:
+ alpha = degrees(atan2(vy, vx))
- if alpha_old == None:
+ if alpha_old is None:
alpha_old = alpha
continue
-
+
# Get difference to previous angle
- alpha_diff = fabs(alpha - alpha_old)
+ alpha_diff = fabs(alpha - alpha_old)
alpha_old = alpha
-
+
# If the new vector differs from the old one, we add the old point
# to the output list, as the line changed it's way :)
if alpha_diff > tolerance:
#print ">",alpha_diff, "\t", vx, vy, l
- p_new.append(vertices[i-1])
+ p_new.append(vertices[i - 1])
# We also want to append the last point :)
p_new.append(vertices[-1])
-
+
# Returns: (1) New Pointlist, (2) Soft reduced pointlist (reduce_poly())
return p_new, vxx
-
-
-# The following functions is_left, reduce_poly and convex_hull are
+
+
+# The following functions is_left, reduce_poly and convex_hull are
# from the pymunk project (http://code.google.com/p/pymunk/)
def is_left(p0, p1, p2):
"""Test if p2 is left, on or right of the (infinite) line (p0,p1).
-
+
:return: > 0 for p2 left of the line through p0 and p1
= 0 for p2 on the line
< 0 for p2 right of the line
"""
- sorting = (p1[0] - p0[0])*(p2[1]-p0[1]) - (p2[0]-p0[0])*(p1[1]-p0[1])
- if sorting > 0: return 1
- elif sorting < 0: return -1
- else: return 0
-
-def is_convex(points):
- """Test if a polygon (list of (x,y)) is strictly convex or not.
-
- :return: True if the polygon is convex, False otherwise
- """
+ sorting = (p1[0] - p0[0]) * (p2[1] - p0[1]) - (p2[0] - p0[0]) * \
+ (p1[1] - p0[1])
+ if sorting > 0:
+ return 1
+ elif sorting < 0:
+ return -1
+ else:
+ return 0
+
+
+def is_convex(points):
+ """Test if a polygon (list of (x,y)) is strictly convex or not.
+
+ :return: True if the polygon is convex, False otherwise
+ """
#assert len(points) > 2, "not enough points to form a polygon"
-
- p0 = points[0]
- p1 = points[1]
+
+ p0 = points[0]
+ p1 = points[1]
p2 = points[2]
- xc, yc = 0, 0
- is_same_winding = is_left(p0, p1, p2)
- for p2 in points[2:] + [p0] + [p1]:
- if is_same_winding != is_left(p0, p1, p2):
- return False
- a = p1[0] - p0[0], p1[1] - p0[1] # p1-p0
- b = p2[0] - p1[0], p2[1] - p1[1] # p2-p1
- if sign(a[0]) != sign(b[0]): xc +=1
- if sign(a[1]) != sign(b[1]): yc +=1
- p0, p1 = p1, p2
-
- return xc <= 2 and yc <= 2
-
-def sign(x):
- if x < 0: return -1
- else: return 1
-
+ xc, yc = 0, 0
+ is_same_winding = is_left(p0, p1, p2)
+ for p2 in points[2:] + [p0] + [p1]:
+ if is_same_winding != is_left(p0, p1, p2):
+ return False
+ a = p1[0] - p0[0], p1[1] - p0[1] # p1-p0
+ b = p2[0] - p1[0], p2[1] - p1[1] # p2-p1
+ if sign(a[0]) != sign(b[0]):
+ xc += 1
+ if sign(a[1]) != sign(b[1]):
+ yc += 1
+ p0, p1 = p1, p2
+
+ return xc <= 2 and yc <= 2
+
+
+def sign(x):
+ if x < 0:
+ return -1
+ else:
+ return 1
+
def reduce_poly(points, tolerance=50):
"""Remove close points to simplify a polyline
tolerance is the min distance between two points squared.
-
+
:return: The reduced polygon as a list of (x,y)
"""
curr_p = points[0]
reduced_ps = [points[0]]
-
+
for p in points[1:]:
x1, y1 = curr_p
x2, y2 = p
dx = fabs(x2 - x1)
dy = fabs(y2 - y1)
- l = sqrt((dx*dx) + (dy*dy))
+ l = sqrt((dx * dx) + (dy * dy))
if l > tolerance:
curr_p = p
reduced_ps.append(p)
-
+
return reduced_ps
+
def convex_hull(points):
"""Create a convex hull from a list of points.
This function uses the Graham Scan Algorithm.
-
+
:return: Convex hull as a list of (x,y)
"""
### Find lowest rightmost point
@@ -319,21 +340,21 @@ def convex_hull(points):
elif p[1] == p0[1] and p[0] > p0[0]:
p0 = p
points.remove(p0)
-
+
### Sort the points angularly about p0 as center
f = partial(is_left, p0)
- points.sort(cmp = f)
+ points.sort(cmp=f)
points.reverse()
points.insert(0, p0)
-
+
### Find the hull points
hull = [p0, points[1]]
-
+
for p in points[2:]:
-
+
pt1 = hull[-1]
pt2 = hull[-2]
- l = is_left(pt2, pt1, p)
+ l = is_left(pt2, pt1, p)
if l > 0:
hull.append(p)
else:
@@ -343,5 +364,4 @@ def convex_hull(points):
pt2 = hull[-2]
l = is_left(pt2, pt1, p)
hull.append(p)
- return hull
-
+ return hull