Mercurial > traipse_dev
comparison orpg/mapper/grid.py @ 0:4385a7d0efd1 grumpy-goblin
Deleted and repushed it with the 'grumpy-goblin' branch. I forgot a y
author | sirebral |
---|---|
date | Tue, 14 Jul 2009 16:41:58 -0500 |
parents | |
children | 78407d627cba |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:4385a7d0efd1 |
---|---|
1 # Copyright (C) 2000-2001 The OpenRPG Project | |
2 # | |
3 # openrpg-dev@lists.sourceforge.net | |
4 # | |
5 # This program is free software; you can redistribute it and/or modify | |
6 # it under the terms of the GNU General Public License as published by | |
7 # the Free Software Foundation; either version 2 of the License, or | |
8 # (at your option) any later version. | |
9 # | |
10 # This program is distributed in the hope that it will be useful, | |
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
13 # GNU General Public License for more details. | |
14 # | |
15 # You should have received a copy of the GNU General Public License | |
16 # along with this program; if not, write to the Free Software | |
17 # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |
18 # -- | |
19 # | |
20 # File: mapper/gird.py | |
21 # Author: OpenRPG Team | |
22 # Maintainer: | |
23 # Version: | |
24 # $Id: grid.py,v 1.29 2007/12/07 20:39:49 digitalxero Exp $ | |
25 # | |
26 # Description: | |
27 # | |
28 __version__ = "$Id: grid.py,v 1.29 2007/12/07 20:39:49 digitalxero Exp $" | |
29 | |
30 from base import * | |
31 from isometric import * | |
32 from miniatures import SNAPTO_ALIGN_CENTER | |
33 from miniatures import SNAPTO_ALIGN_TL | |
34 from math import floor | |
35 | |
36 # Grid mode constants | |
37 GRID_RECTANGLE = 0 | |
38 GRID_HEXAGON = 1 | |
39 GRID_ISOMETRIC = 2 | |
40 LINE_NONE = 0 | |
41 LINE_DOTTED = 1 | |
42 LINE_SOLID = 2 | |
43 RATIO_DEFAULT = 2.0 | |
44 | |
45 ##----------------------------- | |
46 ## grid layer | |
47 ##----------------------------- | |
48 class grid_layer(layer_base): | |
49 | |
50 def __init__(self, canvas): | |
51 layer_base.__init__(self) | |
52 self.canvas = canvas | |
53 self.iso_ratio = RATIO_DEFAULT #2:1 isometric ratio | |
54 self.mapscale = 1.0 | |
55 self.unit_size = 100 | |
56 self.unit_size_y = 100 | |
57 #unit_widest and unit_offset are for the Hex Grid only. these are mathmatics to figure out the exact center of the hex | |
58 self.unit_widest = 100 | |
59 self.unit_offset = 100 | |
60 #size_ratio is the size ajustment for Hex and ISO to make them more accurate | |
61 self.size_ratio = 1.5 | |
62 self.snap = True | |
63 self.color = wx.BLACK# = color.Get() | |
64 #self.color = cmpColour(r,g,b) | |
65 self.r_h = RGBHex() | |
66 self.mode = GRID_RECTANGLE | |
67 self.line = LINE_NONE | |
68 # Keep logic for different modes in different functions | |
69 self.grid_hit_test = self.grid_hit_test_rect | |
70 self.get_top_corner = self.get_top_corner_rect | |
71 self.layerDraw = self.draw_rect | |
72 self.isUpdated = True | |
73 | |
74 def get_unit_size(self): | |
75 return self.unit_size | |
76 | |
77 def get_iso_ratio(self): | |
78 return self.iso_ratio | |
79 | |
80 def get_mode(self): | |
81 return self.mode | |
82 | |
83 def get_color(self): | |
84 return self.color | |
85 | |
86 def get_line_type(self): | |
87 return self.line | |
88 | |
89 def is_snap(self): | |
90 return self.snap | |
91 | |
92 def get_snapped_to_pos(self, pos, snap_to_align, mini_width, mini_height): | |
93 grid_pos = self.grid_hit_test(pos) | |
94 if grid_pos is not None: | |
95 topLeft = self.get_top_corner(grid_pos)# get the top corner for this grid cell | |
96 if snap_to_align == SNAPTO_ALIGN_CENTER: | |
97 if self.mode == GRID_HEXAGON: | |
98 x = topLeft.x + (((self.unit_size/1.75) - mini_width) /2) | |
99 y = topLeft.y + ((self.unit_size - mini_height) /2) | |
100 elif self.mode == GRID_ISOMETRIC: | |
101 x = (topLeft.x)-(mini_width/2) | |
102 y = (topLeft.y)-(mini_height) | |
103 else:# GRID_RECTANGLE | |
104 x = topLeft.x + ((self.unit_size - mini_width) / 2) | |
105 y = topLeft.y + ((self.unit_size_y - mini_height) /2) | |
106 else: | |
107 x = topLeft.x | |
108 y = topLeft.y | |
109 return cmpPoint(int(x),int(y)) # Set the pos attribute | |
110 else: | |
111 return cmpPoint(int(pos.x),int(pos.y)) | |
112 | |
113 def set_rect_mode(self): | |
114 "switch grid to rectangular mode" | |
115 self.mode = GRID_RECTANGLE | |
116 self.grid_hit_test = self.grid_hit_test_rect | |
117 self.get_top_corner = self.get_top_corner_rect | |
118 self.layerDraw = self.draw_rect | |
119 self.unit_size_y = self.unit_size | |
120 | |
121 def set_hex_mode(self): | |
122 "switch grid to hexagonal mode" | |
123 self.mode = GRID_HEXAGON | |
124 self.grid_hit_test = self.grid_hit_test_hex | |
125 self.get_top_corner = self.get_top_corner_hex | |
126 self.layerDraw = self.draw_hex | |
127 self.unit_size_y = self.unit_size | |
128 self.unit_offset = sqrt(pow((self.unit_size/self.size_ratio ),2)-pow((self.unit_size/2),2)) | |
129 self.unit_widest = (self.unit_offset*2)+(self.unit_size/self.size_ratio ) | |
130 | |
131 def set_iso_mode(self): | |
132 "switch grid to hexagonal mode" | |
133 self.mode = GRID_ISOMETRIC | |
134 self.grid_hit_test = self.grid_hit_test_iso | |
135 self.get_top_corner = self.get_top_corner_iso | |
136 self.layerDraw = self.draw_iso | |
137 self.unit_size_y = self.unit_size | |
138 | |
139 def set_line_none(self): | |
140 "switch to no line mode for grid" | |
141 self.line = LINE_NONE | |
142 | |
143 def set_line_dotted(self): | |
144 "switch to dotted line mode for grid" | |
145 self.line = LINE_DOTTED | |
146 | |
147 def set_line_solid(self): | |
148 "switch to solid line mode for grid" | |
149 self.line = LINE_SOLID | |
150 | |
151 def grid_hit_test_rect(self,pos): | |
152 "return grid pos (w,h) on rect map from pos" | |
153 if self.unit_size and self.snap: | |
154 return cmpPoint(int(pos.x/self.unit_size), int(pos.y/self.unit_size)) | |
155 else: | |
156 return None | |
157 | |
158 def grid_hit_test_hex(self,pos): | |
159 "return grid pos (w,h) on hex map from pos" | |
160 if self.unit_size and self.snap: | |
161 # rectangular repeat patern is as follows (unit_size is the height of a hex) | |
162 hex_side = int(self.unit_size/1.75) | |
163 half_height = int(self.unit_size/2) | |
164 height = int(self.unit_size) | |
165 #_____ | |
166 # \ / | |
167 # \_____/ | |
168 # / \ | |
169 #_____/ \ | |
170 col = int(pos.x/(hex_side*1.5)) | |
171 row = int(pos.y/height) | |
172 (px, py) = (pos.x-(col*(hex_side*1.5)), pos.y-(row*height)) | |
173 # adjust for the odd columns' rows being staggered lower | |
174 if col % 2 == 1: | |
175 if py < half_height: | |
176 row = row - 1 | |
177 py = py + half_height | |
178 else: | |
179 py = py - half_height | |
180 # adjust for top right corner | |
181 if (px * height - py * hex_side) > height * hex_side: | |
182 if col % 2 == 0: | |
183 row = row - 1 | |
184 col = col + 1 | |
185 # adjust for bottom right corner | |
186 elif (px * height + py * hex_side) > 2 * height * hex_side: | |
187 if col%2==1: | |
188 row = row + 1 | |
189 col = col + 1 | |
190 return cmpPoint(col, row) | |
191 else: | |
192 return None | |
193 | |
194 def grid_hit_test_iso(self,pos): | |
195 "return grid pos (w,h) on isometric map from pos" | |
196 if self.unit_size and self.snap: | |
197 height = self.unit_size*self.size_ratio/self.iso_ratio | |
198 width = self.unit_size*self.size_ratio | |
199 iso_unit_size = height * width | |
200 # convert to isometric pos which has an origin of cell (0,0) | |
201 # x-ord increasing as you go up and right, y-ord increasing as you go down and right | |
202 # this is the transformation from grid co-ord to iso co-ords | |
203 iso_x = (pos.x*height) - (pos.y*width) + (iso_unit_size/2) | |
204 iso_y = (pos.x*height) + (pos.y*width) - (iso_unit_size/2) | |
205 # | |
206 # /\ | |
207 # / \ | |
208 #/ \ | |
209 #\ / | |
210 # \ / | |
211 # \/ | |
212 # so the exact isomorphic (0,0) is the left corner of the first (ie. top left) diamond | |
213 # this is at grid co-ordinate (0, height/2) | |
214 # the top corner of the first diamond is grid co-ord (width/2, 0) | |
215 # and therefore (per transformation above) is at iso co-ord (iso_unit_size, 0) | |
216 # the bottom corner of the first diamond is grid co-ord (width/2, height) | |
217 # and therefore (per transformation above) is at iso co-ord (0, iso_unit_size) | |
218 | |
219 # the calculation is now as simple as the rectangle case, but using iso co-ords | |
220 return cmpPoint(floor(iso_x/iso_unit_size), floor(iso_y/iso_unit_size)) | |
221 else: | |
222 return None | |
223 | |
224 def get_top_corner_iso(self, iso_pos): | |
225 "return upper left of a iso grid pos" | |
226 # for whatever reason the iso grid returns the center of the diamond for "top left corner" | |
227 if self.unit_size: | |
228 half_height = self.unit_size*self.size_ratio/(2*self.iso_ratio) | |
229 half_width = self.unit_size*self.size_ratio/2 | |
230 # convert back into grid co-ordinates of center of diamond | |
231 grid_x = (iso_pos.y*half_width) + (iso_pos.x*half_width) + half_width | |
232 grid_y = (iso_pos.y*half_height) - (iso_pos.x*half_height) + half_height | |
233 return cmpPoint(int(grid_x), int(grid_y)) | |
234 else: | |
235 return None | |
236 | |
237 def get_top_corner_rect(self,grid_pos): | |
238 "return upper left of a rect grid pos" | |
239 if self.unit_size: | |
240 return cmpPoint(grid_pos[0]*self.unit_size,grid_pos[1]*self.unit_size) | |
241 else: | |
242 return None | |
243 | |
244 def get_top_corner_hex(self,grid_pos): | |
245 "return upper left of a hex grid pos" | |
246 if self.unit_size: | |
247 # We can get our x value directly, y is trickier | |
248 temp_x = (((self.unit_size/1.75)*1.5)*grid_pos[0]) | |
249 temp_y = self.unit_size*grid_pos[1] | |
250 # On odd columns we have to slide down slightly | |
251 if grid_pos[0] % 2: | |
252 temp_y += self.unit_size/2 | |
253 return cmpPoint(temp_x,temp_y) | |
254 else: | |
255 return None | |
256 | |
257 def set_grid(self, unit_size, snap, color, mode, line, ratio=None): | |
258 self.unit_size = unit_size | |
259 if ratio != None: | |
260 self.iso_ratio = ratio | |
261 self.snap = snap | |
262 self.set_color(color) | |
263 self.SetMode(mode) | |
264 self.SetLine(line) | |
265 | |
266 def SetLine(self,line): | |
267 if line == LINE_NONE: | |
268 self.set_line_none() | |
269 elif line == LINE_DOTTED: | |
270 self.set_line_dotted() | |
271 elif line == LINE_SOLID: | |
272 self.set_line_solid() | |
273 | |
274 def SetMode(self, mode): | |
275 if mode == GRID_RECTANGLE: | |
276 self.set_rect_mode() | |
277 elif mode == GRID_HEXAGON: | |
278 self.set_hex_mode() | |
279 elif mode == GRID_ISOMETRIC: | |
280 self.set_iso_mode() | |
281 | |
282 def return_grid(self): | |
283 return self.canvas.size | |
284 | |
285 def set_color(self,color): | |
286 (r,g,b) = color.Get() | |
287 self.color = cmpColour(r,g,b) | |
288 | |
289 def draw_iso(self,dc,topleft,clientsize): | |
290 if not self.unit_size: return | |
291 if self.line == LINE_NONE: return | |
292 if self.line == LINE_SOLID: | |
293 dc.SetPen(wx.Pen(self.color,1,wx.SOLID)) | |
294 else: | |
295 dc.SetPen(wx.Pen(self.color,1,wx.DOT)) | |
296 sz = self.canvas.size | |
297 | |
298 # Enable DC optimizations if available on a platform | |
299 dc.BeginDrawing() | |
300 | |
301 # create IsoGrid helper object | |
302 IG = IsoGrid(self.unit_size*self.size_ratio) | |
303 IG.Ratio(self.iso_ratio) | |
304 rows = int(min(clientsize[1]+topleft[1],sz[1])/IG.height) | |
305 cols = int(min(clientsize[0]+topleft[0],sz[0])/IG.width) | |
306 for y in range(rows+1): | |
307 for x in range(cols+1): | |
308 IG.BoundPlace((x*IG.width),(y*IG.height)) | |
309 x1,y1 = IG.Top() | |
310 x2,y2 = IG.Left() | |
311 dc.DrawLine(x1,y1,x2,y2) | |
312 x1,y1 = IG.Left() | |
313 x2,y2 = IG.Bottom() | |
314 dc.DrawLine(x1,y1,x2,y2) | |
315 x1,y1 = IG.Bottom() | |
316 x2,y2 = IG.Right() | |
317 dc.DrawLine(x1,y1,x2,y2) | |
318 x1,y1 = IG.Right() | |
319 x2,y2 = IG.Top() | |
320 dc.DrawLine(x1,y1,x2,y2) | |
321 # Enable DC optimizations if available on a platform | |
322 dc.EndDrawing() | |
323 dc.SetPen(wx.NullPen) | |
324 # Disable pen/brush optimizations to prevent any odd effects elsewhere | |
325 | |
326 def draw_rect(self,dc,topleft,clientsize): | |
327 if self.unit_size: | |
328 draw = 1 | |
329 # Enable pen/brush optimizations if available on a platform | |
330 if self.line == LINE_NONE: | |
331 draw = 0 | |
332 elif self.line == LINE_SOLID: | |
333 dc.SetPen(wx.Pen(self.color,1,wx.SOLID)) | |
334 else: | |
335 dc.SetPen(wx.Pen(self.color,1,wx.DOT)) | |
336 if draw: | |
337 sz = self.canvas.size | |
338 # Enable DC optimizations if available on a platform | |
339 dc.BeginDrawing() | |
340 # Now, draw the map grid | |
341 x = 0 | |
342 s = self.unit_size | |
343 x = int(topleft[0]/s)*s | |
344 mx = min(clientsize[0]+topleft[0],sz[0]) | |
345 my = min(clientsize[1]+topleft[1],sz[1]) | |
346 while x < mx: | |
347 dc.DrawLine(x,topleft[1],x,my) | |
348 x += self.unit_size | |
349 y = 0 | |
350 y = int (topleft[1]/s)*s | |
351 while y < my: | |
352 dc.DrawLine(topleft[0],y,mx,y) | |
353 y += self.unit_size | |
354 # Enable DC optimizations if available on a platform | |
355 dc.EndDrawing() | |
356 dc.SetPen(wx.NullPen) | |
357 # Disable pen/brush optimizations to prevent any odd effects elsewhere | |
358 | |
359 def draw_hex(self,dc,topleft,clientsize): | |
360 if self.unit_size: | |
361 draw = 1 | |
362 # Enable pen/brush optimizations if available on a platform | |
363 if self.line == LINE_NONE: | |
364 draw = 0 | |
365 elif self.line == LINE_SOLID: | |
366 dc.SetPen(wx.Pen(self.color,1,wx.SOLID)) | |
367 else: | |
368 dc.SetPen(wx.Pen(self.color,1,wx.DOT)) | |
369 if draw: | |
370 sz = self.canvas.size | |
371 x = 0 | |
372 A = self.unit_size/1.75 #Side Length | |
373 B = self.unit_size #The width between any two sides | |
374 D = self.unit_size/2 #The distance from the top to the middle of the hex | |
375 C = self.unit_size/3.5 #The distance from the point of the hex to the point where the top line starts | |
376 | |
377 # _____ | |
378 # / \ | |
379 # / \ | |
380 # \ / | |
381 # \_____/ | |
382 | |
383 startx=int(topleft[0]/(3*A))*(3*A) | |
384 starty=int(topleft[1]/B)*B | |
385 y = starty | |
386 mx = min(clientsize[0]+topleft[0],sz[0]) | |
387 my = min(clientsize[1]+topleft[1],sz[1]) | |
388 while y < my: | |
389 x = startx | |
390 lineArray = [] | |
391 while x < mx: | |
392 #The top / Bottom of the Hex | |
393 lineArray.append((x, y)) | |
394 lineArray.append((x+A, y)) | |
395 #The Right Top Side of the Hex | |
396 lineArray.append((x+A, y)) | |
397 lineArray.append((x+A+C, y+D)) | |
398 #The Right Bottom Side of the Hex | |
399 lineArray.append((x+A+C, y+D)) | |
400 lineArray.append((x+A, y+B)) | |
401 #The Top / of the Middle Hex | |
402 lineArray.append((x+A+C, y+D)) | |
403 lineArray.append((x+A+C+A, y+D)) | |
404 #The Left Bottom Side of the Hex | |
405 lineArray.append((x+A+C+A, y+D)) | |
406 lineArray.append((x+A+C+A+C, y+B)) | |
407 #The left Top Side of the Hex | |
408 lineArray.append((x+A+C+A, y+D)) | |
409 lineArray.append((x+A+C+A+C, y)) | |
410 x += A*3 | |
411 y += B | |
412 dc.DrawLines(lineArray) | |
413 dc.SetPen(wx.NullPen) | |
414 # Disable pen/brush optimizations to prevent any odd effects elsewhere | |
415 | |
416 def layerToXML(self,action = "update"): | |
417 xml_str = "<grid" | |
418 if self.color != None: | |
419 (red,green,blue) = self.color.Get() | |
420 hexcolor = self.r_h.hexstring(red, green, blue) | |
421 xml_str += " color='" + hexcolor + "'" | |
422 if self.unit_size != None: | |
423 xml_str += " size='" + str(self.unit_size) + "'" | |
424 if self.iso_ratio != None: | |
425 xml_str += " ratio='" + str(self.iso_ratio) + "'" | |
426 if self.snap != None: | |
427 if self.snap: | |
428 xml_str += " snap='1'" | |
429 else: | |
430 xml_str += " snap='0'" | |
431 if self.mode != None: | |
432 xml_str+= " mode='" + str(self.mode) + "'" | |
433 if self.line != None: | |
434 xml_str+= " line='" + str(self.line) + "'" | |
435 xml_str += "/>" | |
436 if (action == "update" and self.isUpdated) or action == "new": | |
437 self.isUpdated = False | |
438 return xml_str | |
439 else: | |
440 return '' | |
441 | |
442 def layerTakeDOM(self, xml_dom): | |
443 if xml_dom.hasAttribute("color"): | |
444 r,g,b = self.r_h.rgb_tuple(xml_dom.getAttribute("color")) | |
445 self.set_color(cmpColour(r,g,b)) | |
446 #backwards compatible with non-isometric map formated clients | |
447 ratio = RATIO_DEFAULT | |
448 if xml_dom.hasAttribute("ratio"): | |
449 ratio = xml_dom.getAttribute("ratio") | |
450 if xml_dom.hasAttribute("mode"): | |
451 self.SetMode(int(xml_dom.getAttribute("mode"))) | |
452 if xml_dom.hasAttribute("size"): | |
453 self.unit_size = int(xml_dom.getAttribute("size")) | |
454 self.unit_size_y = self.unit_size | |
455 if xml_dom.hasAttribute("snap"): | |
456 if (xml_dom.getAttribute("snap") == 'True') or (xml_dom.getAttribute("snap") == "1"): | |
457 self.snap = True | |
458 else: | |
459 self.snap = False | |
460 if xml_dom.hasAttribute("line"): | |
461 self.SetLine(int(xml_dom.getAttribute("line"))) |