diff 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
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/orpg/mapper/grid.py	Tue Jul 14 16:41:58 2009 -0500
@@ -0,0 +1,461 @@
+# Copyright (C) 2000-2001 The OpenRPG Project
+#
+#    openrpg-dev@lists.sourceforge.net
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+# --
+#
+# File: mapper/gird.py
+# Author: OpenRPG Team
+# Maintainer:
+# Version:
+#   $Id: grid.py,v 1.29 2007/12/07 20:39:49 digitalxero Exp $
+#
+# Description:
+#
+__version__ = "$Id: grid.py,v 1.29 2007/12/07 20:39:49 digitalxero Exp $"
+
+from base import *
+from isometric import *
+from miniatures import SNAPTO_ALIGN_CENTER
+from miniatures import SNAPTO_ALIGN_TL
+from math import floor
+
+# Grid mode constants
+GRID_RECTANGLE = 0
+GRID_HEXAGON = 1
+GRID_ISOMETRIC = 2
+LINE_NONE = 0
+LINE_DOTTED = 1
+LINE_SOLID = 2
+RATIO_DEFAULT = 2.0
+
+##-----------------------------
+## grid layer
+##-----------------------------
+class grid_layer(layer_base):
+
+    def __init__(self, canvas):
+        layer_base.__init__(self)
+        self.canvas = canvas
+        self.iso_ratio = RATIO_DEFAULT  #2:1 isometric ratio
+        self.mapscale = 1.0
+        self.unit_size = 100
+        self.unit_size_y = 100
+        #unit_widest and unit_offset are for the Hex Grid only. these are mathmatics to figure out the exact center of the hex
+        self.unit_widest = 100
+        self.unit_offset = 100
+        #size_ratio is the size ajustment for Hex and ISO to make them more accurate
+        self.size_ratio = 1.5
+        self.snap = True
+        self.color = wx.BLACK# = color.Get()
+        #self.color = cmpColour(r,g,b)
+        self.r_h = RGBHex()
+        self.mode = GRID_RECTANGLE
+        self.line = LINE_NONE
+        # Keep logic for different modes in different functions
+        self.grid_hit_test = self.grid_hit_test_rect
+        self.get_top_corner = self.get_top_corner_rect
+        self.layerDraw = self.draw_rect
+        self.isUpdated = True
+
+    def get_unit_size(self):
+        return self.unit_size
+
+    def get_iso_ratio(self):
+        return self.iso_ratio
+
+    def get_mode(self):
+        return self.mode
+
+    def get_color(self):
+        return self.color
+
+    def get_line_type(self):
+        return self.line
+
+    def is_snap(self):
+        return self.snap
+
+    def get_snapped_to_pos(self, pos, snap_to_align, mini_width, mini_height):
+        grid_pos = self.grid_hit_test(pos)
+        if grid_pos is not None:
+            topLeft = self.get_top_corner(grid_pos)#  get the top corner for this grid cell
+            if snap_to_align == SNAPTO_ALIGN_CENTER:
+                if self.mode == GRID_HEXAGON:
+                    x = topLeft.x + (((self.unit_size/1.75) - mini_width) /2)
+                    y = topLeft.y + ((self.unit_size - mini_height) /2)
+                elif self.mode == GRID_ISOMETRIC:
+                    x = (topLeft.x)-(mini_width/2)
+                    y = (topLeft.y)-(mini_height)
+                else:# GRID_RECTANGLE
+                    x = topLeft.x + ((self.unit_size - mini_width) / 2)
+                    y = topLeft.y + ((self.unit_size_y - mini_height) /2)
+            else:
+                x = topLeft.x
+                y = topLeft.y
+            return cmpPoint(int(x),int(y))                                           #  Set the pos attribute
+        else:
+            return cmpPoint(int(pos.x),int(pos.y))
+
+    def set_rect_mode(self):
+        "switch grid to rectangular mode"
+        self.mode = GRID_RECTANGLE
+        self.grid_hit_test = self.grid_hit_test_rect
+        self.get_top_corner = self.get_top_corner_rect
+        self.layerDraw = self.draw_rect
+        self.unit_size_y = self.unit_size
+
+    def set_hex_mode(self):
+        "switch grid to hexagonal mode"
+        self.mode = GRID_HEXAGON
+        self.grid_hit_test = self.grid_hit_test_hex
+        self.get_top_corner = self.get_top_corner_hex
+        self.layerDraw = self.draw_hex
+        self.unit_size_y = self.unit_size
+        self.unit_offset = sqrt(pow((self.unit_size/self.size_ratio ),2)-pow((self.unit_size/2),2))
+        self.unit_widest = (self.unit_offset*2)+(self.unit_size/self.size_ratio )
+
+    def set_iso_mode(self):
+        "switch grid to hexagonal mode"
+        self.mode = GRID_ISOMETRIC
+        self.grid_hit_test = self.grid_hit_test_iso
+        self.get_top_corner = self.get_top_corner_iso
+        self.layerDraw = self.draw_iso
+        self.unit_size_y = self.unit_size
+
+    def set_line_none(self):
+        "switch to no line mode for grid"
+        self.line = LINE_NONE
+
+    def set_line_dotted(self):
+        "switch to dotted line mode for grid"
+        self.line = LINE_DOTTED
+
+    def set_line_solid(self):
+        "switch to solid line mode for grid"
+        self.line = LINE_SOLID
+
+    def grid_hit_test_rect(self,pos):
+        "return grid pos (w,h) on rect map from pos"
+        if self.unit_size and self.snap:
+            return cmpPoint(int(pos.x/self.unit_size), int(pos.y/self.unit_size))
+        else:
+            return None
+
+    def grid_hit_test_hex(self,pos):
+        "return grid pos (w,h) on hex map from pos"
+        if self.unit_size and self.snap:
+            # rectangular repeat patern is as follows (unit_size is the height of a hex)
+            hex_side = int(self.unit_size/1.75)
+            half_height = int(self.unit_size/2)
+            height = int(self.unit_size)
+            #_____
+            #     \       /
+            #      \_____/
+            #      /     \
+            #_____/       \
+            col = int(pos.x/(hex_side*1.5))
+            row = int(pos.y/height)
+            (px, py) = (pos.x-(col*(hex_side*1.5)), pos.y-(row*height))
+            # adjust for the odd columns' rows being staggered lower
+            if col % 2 == 1:
+                if py < half_height:
+                    row = row - 1
+                    py = py + half_height
+                else:
+                    py = py - half_height
+            # adjust for top right corner
+            if (px * height - py * hex_side) > height * hex_side:
+                if col % 2 == 0:
+                    row = row - 1
+                col = col + 1
+            # adjust for bottom right corner
+            elif (px * height + py * hex_side) > 2 * height * hex_side:
+                if col%2==1:
+                    row = row + 1
+                col = col + 1
+            return cmpPoint(col, row)
+        else:
+            return None
+
+    def grid_hit_test_iso(self,pos):
+        "return grid pos (w,h) on isometric map from pos"
+        if self.unit_size and self.snap:
+            height = self.unit_size*self.size_ratio/self.iso_ratio
+            width = self.unit_size*self.size_ratio
+            iso_unit_size = height * width
+            # convert to isometric pos which has an origin of cell (0,0)
+            # x-ord increasing as you go up and right, y-ord increasing as you go down and right
+            # this is the transformation from grid co-ord to iso co-ords
+            iso_x = (pos.x*height) - (pos.y*width) + (iso_unit_size/2)
+            iso_y = (pos.x*height) + (pos.y*width) - (iso_unit_size/2)
+            #
+            #  /\
+            # /  \
+            #/    \
+            #\    /
+            # \  /
+            #  \/
+            # so the exact isomorphic (0,0) is the left corner of the first (ie. top left) diamond
+            # this is at grid co-ordinate (0, height/2)
+            # the top corner of the first diamond is grid co-ord (width/2, 0)
+            # and therefore (per transformation above) is at iso co-ord (iso_unit_size, 0)
+            # the bottom corner of the first diamond is grid co-ord (width/2, height)
+            # and therefore (per transformation above) is at iso co-ord (0, iso_unit_size)
+
+            # the calculation is now as simple as the rectangle case, but using iso co-ords
+            return cmpPoint(floor(iso_x/iso_unit_size), floor(iso_y/iso_unit_size))
+        else:
+            return None
+
+    def get_top_corner_iso(self, iso_pos):
+        "return upper left of a iso grid pos"
+        # for whatever reason the iso grid returns the center of the diamond for "top left corner"
+        if self.unit_size:
+            half_height = self.unit_size*self.size_ratio/(2*self.iso_ratio)
+            half_width = self.unit_size*self.size_ratio/2
+            # convert back into grid co-ordinates of center of diamond
+            grid_x = (iso_pos.y*half_width) + (iso_pos.x*half_width) + half_width
+            grid_y = (iso_pos.y*half_height) - (iso_pos.x*half_height) + half_height
+            return cmpPoint(int(grid_x), int(grid_y))
+        else:
+            return None
+
+    def get_top_corner_rect(self,grid_pos):
+        "return upper left of a rect grid pos"
+        if self.unit_size:
+            return cmpPoint(grid_pos[0]*self.unit_size,grid_pos[1]*self.unit_size)
+        else:
+            return None
+
+    def get_top_corner_hex(self,grid_pos):
+        "return upper left of a hex grid pos"
+        if self.unit_size:
+            # We can get our x value directly, y is trickier
+            temp_x = (((self.unit_size/1.75)*1.5)*grid_pos[0])
+            temp_y = self.unit_size*grid_pos[1]
+            # On odd columns we have to slide down slightly
+            if grid_pos[0] % 2:
+                temp_y += self.unit_size/2
+            return cmpPoint(temp_x,temp_y)
+        else:
+            return None
+
+    def set_grid(self, unit_size, snap, color, mode, line, ratio=None):
+        self.unit_size = unit_size
+        if ratio != None:
+            self.iso_ratio = ratio
+        self.snap = snap
+        self.set_color(color)
+        self.SetMode(mode)
+        self.SetLine(line)
+
+    def SetLine(self,line):
+        if line == LINE_NONE:
+            self.set_line_none()
+        elif line == LINE_DOTTED:
+            self.set_line_dotted()
+        elif line == LINE_SOLID:
+            self.set_line_solid()
+
+    def SetMode(self, mode):
+        if mode == GRID_RECTANGLE:
+            self.set_rect_mode()
+        elif mode == GRID_HEXAGON:
+            self.set_hex_mode()
+        elif mode == GRID_ISOMETRIC:
+            self.set_iso_mode()
+
+    def return_grid(self):
+        return self.canvas.size
+
+    def set_color(self,color):
+        (r,g,b) = color.Get()
+        self.color = cmpColour(r,g,b)
+
+    def draw_iso(self,dc,topleft,clientsize):
+        if not self.unit_size: return
+        if self.line == LINE_NONE: return
+        if self.line == LINE_SOLID:
+            dc.SetPen(wx.Pen(self.color,1,wx.SOLID))
+        else:
+            dc.SetPen(wx.Pen(self.color,1,wx.DOT))
+        sz = self.canvas.size
+
+        # Enable DC optimizations if available on a platform
+        dc.BeginDrawing()
+
+        # create IsoGrid helper object
+        IG = IsoGrid(self.unit_size*self.size_ratio)
+        IG.Ratio(self.iso_ratio)
+        rows = int(min(clientsize[1]+topleft[1],sz[1])/IG.height)
+        cols = int(min(clientsize[0]+topleft[0],sz[0])/IG.width)
+        for y in range(rows+1):
+            for x in range(cols+1):
+                IG.BoundPlace((x*IG.width),(y*IG.height))
+                x1,y1 = IG.Top()
+                x2,y2 = IG.Left()
+                dc.DrawLine(x1,y1,x2,y2)
+                x1,y1 = IG.Left()
+                x2,y2 = IG.Bottom()
+                dc.DrawLine(x1,y1,x2,y2)
+                x1,y1 = IG.Bottom()
+                x2,y2 = IG.Right()
+                dc.DrawLine(x1,y1,x2,y2)
+                x1,y1 = IG.Right()
+                x2,y2 = IG.Top()
+                dc.DrawLine(x1,y1,x2,y2)
+        # Enable DC optimizations if available on a platform
+        dc.EndDrawing()
+        dc.SetPen(wx.NullPen)
+        # Disable pen/brush optimizations to prevent any odd effects elsewhere
+
+    def draw_rect(self,dc,topleft,clientsize):
+        if self.unit_size:
+            draw = 1
+            # Enable pen/brush optimizations if available on a platform
+            if self.line == LINE_NONE:
+                draw = 0
+            elif self.line == LINE_SOLID:
+                dc.SetPen(wx.Pen(self.color,1,wx.SOLID))
+            else:
+                dc.SetPen(wx.Pen(self.color,1,wx.DOT))
+            if draw:
+                sz = self.canvas.size
+                # Enable DC optimizations if available on a platform
+                dc.BeginDrawing()
+                # Now, draw the map grid
+                x = 0
+                s = self.unit_size
+                x = int(topleft[0]/s)*s
+                mx = min(clientsize[0]+topleft[0],sz[0])
+                my = min(clientsize[1]+topleft[1],sz[1])
+                while x < mx:
+                    dc.DrawLine(x,topleft[1],x,my)
+                    x += self.unit_size
+                y = 0
+                y = int (topleft[1]/s)*s
+                while y < my:
+                    dc.DrawLine(topleft[0],y,mx,y)
+                    y += self.unit_size
+                # Enable DC optimizations if available on a platform
+                dc.EndDrawing()
+                dc.SetPen(wx.NullPen)
+            # Disable pen/brush optimizations to prevent any odd effects elsewhere
+
+    def draw_hex(self,dc,topleft,clientsize):
+        if self.unit_size:
+            draw = 1
+            # Enable pen/brush optimizations if available on a platform
+            if self.line == LINE_NONE:
+                draw = 0
+            elif self.line == LINE_SOLID:
+                dc.SetPen(wx.Pen(self.color,1,wx.SOLID))
+            else:
+                dc.SetPen(wx.Pen(self.color,1,wx.DOT))
+            if draw:
+                sz = self.canvas.size
+                x = 0
+                A = self.unit_size/1.75 #Side Length
+                B = self.unit_size #The width between any two sides
+                D = self.unit_size/2 #The distance from the top to the middle of the hex
+                C = self.unit_size/3.5 #The distance from the point of the hex to the point where the top line starts
+
+                #   _____
+                #  /     \
+                # /       \
+                # \       /
+                #  \_____/
+
+                startx=int(topleft[0]/(3*A))*(3*A)
+                starty=int(topleft[1]/B)*B
+                y = starty
+                mx = min(clientsize[0]+topleft[0],sz[0])
+                my = min(clientsize[1]+topleft[1],sz[1])
+                while y < my:
+                    x = startx
+                    lineArray = []
+                    while x < mx:
+                        #The top / Bottom of the Hex
+                        lineArray.append((x, y))
+                        lineArray.append((x+A, y))
+                        #The Right Top Side of the Hex
+                        lineArray.append((x+A, y))
+                        lineArray.append((x+A+C, y+D))
+                        #The Right Bottom Side of the Hex
+                        lineArray.append((x+A+C, y+D))
+                        lineArray.append((x+A, y+B))
+                        #The Top / of the Middle Hex
+                        lineArray.append((x+A+C, y+D))
+                        lineArray.append((x+A+C+A, y+D))
+                        #The Left Bottom Side of the Hex
+                        lineArray.append((x+A+C+A, y+D))
+                        lineArray.append((x+A+C+A+C, y+B))
+                        #The left Top Side of the Hex
+                        lineArray.append((x+A+C+A, y+D))
+                        lineArray.append((x+A+C+A+C, y))
+                        x += A*3
+                    y += B
+                    dc.DrawLines(lineArray)
+                dc.SetPen(wx.NullPen)
+            # Disable pen/brush optimizations to prevent any odd effects elsewhere
+
+    def layerToXML(self,action = "update"):
+        xml_str = "<grid"
+        if self.color != None:
+            (red,green,blue) = self.color.Get()
+            hexcolor = self.r_h.hexstring(red, green, blue)
+            xml_str += " color='" + hexcolor + "'"
+        if self.unit_size != None:
+            xml_str += " size='" + str(self.unit_size) + "'"
+        if self.iso_ratio != None:
+            xml_str += " ratio='" + str(self.iso_ratio) + "'"
+        if self.snap != None:
+            if self.snap:
+                xml_str += " snap='1'"
+            else:
+                xml_str += " snap='0'"
+        if self.mode != None:
+            xml_str+= "  mode='" + str(self.mode) + "'"
+        if self.line != None:
+            xml_str+= " line='" + str(self.line) + "'"
+        xml_str += "/>"
+        if (action == "update" and self.isUpdated) or action == "new":
+            self.isUpdated = False
+            return xml_str
+        else:
+            return ''
+
+    def layerTakeDOM(self, xml_dom):
+        if xml_dom.hasAttribute("color"):
+            r,g,b = self.r_h.rgb_tuple(xml_dom.getAttribute("color"))
+            self.set_color(cmpColour(r,g,b))
+        #backwards compatible with non-isometric map formated clients
+        ratio = RATIO_DEFAULT
+        if xml_dom.hasAttribute("ratio"):
+            ratio = xml_dom.getAttribute("ratio")
+        if xml_dom.hasAttribute("mode"):
+            self.SetMode(int(xml_dom.getAttribute("mode")))
+        if xml_dom.hasAttribute("size"):
+            self.unit_size = int(xml_dom.getAttribute("size"))
+            self.unit_size_y = self.unit_size
+        if xml_dom.hasAttribute("snap"):
+            if (xml_dom.getAttribute("snap") == 'True') or (xml_dom.getAttribute("snap") == "1"):
+                self.snap = True
+            else:
+                self.snap = False
+        if xml_dom.hasAttribute("line"):
+            self.SetLine(int(xml_dom.getAttribute("line")))