-
Notifications
You must be signed in to change notification settings - Fork 0
/
graphics.py
executable file
·244 lines (200 loc) · 8.23 KB
/
graphics.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
'''A simple proceedural style graphics drawing wrapper.
Created for HIT3046 AI for Games by Clinton Woodward cwoodward@swin.edu.au
This module creates a simple object named "egi", which is an instance of the
EasyGraphics interface, as well as making the pyglet key codes avaiable as
KEY.
Note: This has not been designed for performance! In particular, excessive
text drawing will be very expensive. If you need better performance, you
should implement opengl code for yourself.
'''
from pyglet.gl import *
from pyglet import font, media, window, clock
from math import cos, sin, pi
KEY = window.key # the key codes
# Some preset colors
COLOR_NAMES = { 'BLACK': (0.0, 0.0, 0.0, 1),
'WHITE': (1.0, 1.0, 1.0, 1),
'RED': (1.0, 0.0, 0.0, 1),
'GREEN': (0.0, 1.0, 0.0, 1),
'BLUE': (0.0, 0.0 ,1.0, 1),
'GREY': (0.6, 0.6, 0.6, 1),
'PINK': (1.0, 0.7, 0.7, 1),
'YELLOW': (1.0, 1.0, 0.0, 1),
'ORANGE': (1.0, 0.7, 0.0, 1),
'PURPLE': (1.0, 0.0, 0.7, 1),
'BROWN': (0.5, 0.35,0.0, 1),
'AQUA': (0.0, 1.0, 1.0, 1),
'DARK_GREEN': (0.0, 0.4, 0.0, 1),
'LIGHT_BLUE': (0.6, 0.6, 1.0, 1),
'LIGHT_GREY': (0.8, 0.8, 0.8, 1),
'LIGHT_PINK': (1.0, 0.9, 0.9, 1)
}
'''
Color conversion
'''
HEX = '0123456789abcdef'
HEX2 = dict((a+b, HEX.index(a)*16 + HEX.index(b)) for a in HEX for b in HEX)
def hex6(hex):
if(len(hex) == 3):
hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2]
return hex
def rgb(hex):
hex = hex.lower()
hex = hex6(hex)
return (HEX2[hex[0:2]]/255.0, HEX2[hex[2:4]]/255.0, HEX2[hex[4:6]]/255.0)
def rgb255(hex):
hex = hex.lower()
return (HEX2[hex[0:2]], HEX2[hex[2:4]], HEX2[hex[4:6]])
def hex(rgb):
return format((rgb[0]<<16)|(rgb[1]<<8)|rgb[2], '06x')
def rgba(hex, alpha = 1):
val = rgb(hex)
return (val[0], val[1], val[2], alpha)
'''
Simple graphics interface
'''
class EasyGraphics(object):
def __init__(self):
# current "pen" colour of lines
self.pen_color = (1,0,0,1.)
self.stroke = 1.0 # - thickness the default
def InitWithPyglet(self, window):
# stuff that needs to be done *after* the pyglet window is created
self.set_pen_color(self.pen_color)
self.set_stroke(self.stroke)
self.window = window
# prep the text object
self.text = font.Text(font.load('Proxima Nova',12), '', color=(1,1,1,1),
valign='bottom', halign='left')
# prep the quadric object used by glu* functions (circle)
# styles GLU_LINE, GLU_FILL, GLU_SILHOUETTE, GLU_POINT
self.qobj = gluNewQuadric()
gluQuadricDrawStyle(self.qobj, GLU_SILHOUETTE)
def dot(self, x=0, y=0, pos=None, color=None):
''' Draw a single pixel at a given location. will use pos (with x and y
values) if provided. Colour is (R,G,B,A) values 0.0 to 1.0 '''
if pos is not None:
x, y = pos.x, pos.y
if color is not None:
glColor4f(*color)
glBegin(GL_POINTS) # draw points (only one!)
glVertex3f(x, y, 0.0)
glEnd()
def line(self,x1=0,y1=0,x2=0,y2=0,pos1=None,pos2=None):
''' Draw a single line. Either with xy values, or two position (that
contain x and y values). Uses existing colour and stroke values. '''
if pos1 is not None and pos2 is not None:
x1, y1, x2, y2 = pos1.x, pos1.y, pos2.x, pos2.y
glBegin(GL_LINES)
glVertex2f(x1, y1)
glVertex2f(x2, y2)
glEnd()
def line_by_pos(self,pos1,pos2):
''' Draw a single line. Either with xy values, or two position (that
contain x and y values). Uses existing colour and stroke values. '''
x1, y1, x2, y2 = pos1.x, pos1.y, pos2.x, pos2.y
glBegin(GL_LINES)
glVertex2f(x1, y1)
glVertex2f(x2, y2)
glEnd()
''' I added this function to draw unclosed lines cause the others didn't work '''
def unclosed_shape(self, points):
if(len(points) < 2): return
glBegin(GL_LINE_STRIP)
[glVertex2f(p.x, p.y) for p in points]
glEnd()
def polyline(self, points):
if len(points) < 2: return
pts = [(p.x, p.y) for p in points] # convert to list of tuples
pts = ((GLfloat * 2)*len(pts))(*pts) # convert to GLfloat list
glPushClientAttrib(GL_CLIENT_VERTEX_ARRAY_BIT)
glEnableClientState(GL_VERTEX_ARRAY)
glVertexPointer(2,GL_FLOAT, 0, pts)
glDrawArrays(GL_LINE_STRIP, 0, len(pts))
glPopClientAttrib()
glEnd()
def line_with_arrow(self,v1,v2,size):
norm = v2-v1
norm.normalise()
# calculate where arrow is attached
xpoint = v2 - (norm * size)
# calculate the two extra points required to make the arrowhead
ap1 = xpoint + (norm.perp() * 0.4 * size)
ap2 = xpoint - (norm.perp() * 0.4 * size)
# draw line from start to head crossing point
glBegin(GL_LINES)
glVertex2f(v1.x, v1.y)
glVertex2f(xpoint.x, xpoint.y)
glEnd()
# draw triangle for head
self.closed_shape((v2, ap1, ap2), filled=False)
def cross(self, pos, diameter):
d = diameter
x, y = pos.x, pos.y
glBegin(GL_LINES)
# TL to BR
glVertex2f(x-d, y-d)
glVertex2f(x+d, y+d)
# TR to BL
glVertex2f(x+d, y-d)
glVertex2f(x-d, y+d)
glEnd()
def rect(self, left, top, right, bottom, filled=False):
if filled:
glBegin(GL_QUADS)
else:
glBegin(GL_LINE_LOOP)
# A single quad - TL to TR to BR to BL (to TL...)
glVertex2f(left,top)
glVertex2f(right,top)
glVertex2f(right,bottom)
glVertex2f(left,bottom)
glEnd()
def closed_shape(self, points, filled=False):
if len(points) < 2: return
gl_array_type = GL_POLYGON if filled else GL_LINE_LOOP
# convert points to a list of types, then GLfloat list
pts = [(p.x, p.y) for p in points]
pts = ((GLfloat * 2)*len(pts))(*pts)
# tell GL system about the array of points
glPushClientAttrib(GL_CLIENT_VERTEX_ARRAY_BIT)
glEnableClientState(GL_VERTEX_ARRAY)
glVertexPointer(2,GL_FLOAT, 0, pts)
# draw array of points, and clean up
glDrawArrays(gl_array_type, 0, len(pts))
glPopClientAttrib()
def circle(self, pos, radius, filled=False, slices=0):
glPushMatrix()
glTranslatef(pos.x, pos.y, 0.0)
gluDisk(self.qobj, 0, radius, 32, 1) # default style (filled? line?)
glPopMatrix()
# ----- COLOUR/STROKE STUFF -----
def set_pen_color(self, color=None, name=None):
if name is not None:
color = COLOR_NAMES[name]
self.curr_color = color
glColor4f(*self.curr_color)
def red_pen(self): self.set_pen_color(name='RED')
def blue_pen(self): self.set_pen_color(name='BLUE')
def green_pen(self): self.set_pen_color(name='GREEN')
def black_pen(self): self.set_pen_color(name='BLACK')
def white_pen(self): self.set_pen_color(name='WHITE')
def grey_pen(self): self.set_pen_color(name='GREY')
def aqua_pen(self): self.set_pen_color(name='AQUA')
def orange_pen(self): self.set_pen_color(name='ORANGE')
def set_stroke(self, stroke):
self.stroke = stroke
glLineWidth(self.stroke)
# ----- TEXT METHODS -----
def text_color(self,color=None,name=None):
''' Colour is a tuple (R,G,B,A) with values from 0.0 to 1.0 '''
if name is not None:
color = COLOR_NAMES[name]
self.text.color = color #
def text_at_pos(self, x, y, text):
self.text.text = text
self.text.x = x
self.text.y = self.window.height + y if y < 0 else y
self.text.draw()
# create an instance for anyone to use
egi = EasyGraphics()