NovaBryan/EVE_Dump
Log | Files | 📦 下载最新源码 (.zip) | Refs | README

hexMap.py (17960B)


      1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\eve\client\script\ui\shared\maps\hexMap.py
      2 import math
      3 import localization
      4 import trinity
      5 from eve.client.script.ui.shared.maps.mapcommon import LegendItem, COLOR_STANDINGS_BAD, COLOR_STANDINGS_GOOD, COLOR_STANDINGS_NEUTRAL
      6 Y_SCALE = 0.8660254037844386
      7 MAP_HEIGHT = -100.0
      8 TIED_TILE_COLOR = (0.4,
      9  0.4,
     10  0.4,
     11  1.0)
     12 UNCLAIMED_TILE_COLOR = (0.1,
     13  0.1,
     14  0.1,
     15  1.0)
     16 
     17 class HexMapController(object):
     18 
     19     def __init__(self, rootTransform, solarSystemGenerator):
     20         self.rootTransform = rootTransform
     21         self.baseTile = trinity.Load('res:/dx9/model/UI/HexTile.red')
     22         self.tilePool = []
     23         self.layoutCache = {}
     24         self.systemInfoMap = {}
     25         self.aabb = AABB()
     26         systems = []
     27         self.mapHeight = 0.0
     28         for systemID, system in solarSystemGenerator:
     29             scaledSystemPos = system.scaledCenter
     30             factionID = system.factionID
     31             systemInfo = HexSystemInfo(systemID, factionID, scaledSystemPos[0], scaledSystemPos[2])
     32             self.systemInfoMap[systemID] = systemInfo
     33             systems.append(systemInfo)
     34             self.aabb.IncludePoint(systemInfo.point)
     35             self.mapHeight = max(self.mapHeight, scaledSystemPos[1])
     36 
     37         self.systemQuadTree = QuadTree(systems, depth=6, aabb=self.aabb)
     38         self.centerPoint = self.systemQuadTree.center
     39         self.mapHeight *= 1.1
     40         self.legend = []
     41         self.CalculateHexagonParameters()
     42         self.nextTileNum = 0
     43         self.firstUnusedTileNum = None
     44         self.SetupScene()
     45 
     46     def __del__(self):
     47         self.highlightBinding = None
     48         del self.rootTransform.curveSets[:]
     49         self.rootTransform = None
     50 
     51     def SetupScene(self):
     52         curveSet = trinity.TriCurveSet()
     53         curveSet.name = 'HighlighCurveSet'
     54         curveSet.playOnLoad = False
     55         curveSet.Stop()
     56         curveSet.scaledTime = 0.0
     57         self.rootTransform.curveSets.append(curveSet)
     58         curve = trinity.Tr2CurveScalar()
     59         curve.extrapolationAfter = trinity.Tr2CurveExtrapolation.CYCLE
     60         curveSet.curves.append(curve)
     61         curve.AddKey(0.0, 0.0)
     62         curve.AddKey(1.0, 1.0)
     63         curve.AddKey(2.0, 0.0)
     64         self.highlightCurveSet = curveSet
     65         self.highlightCurve = curve
     66         curveSet.Play()
     67 
     68     def CalculateHexagonParameters(self):
     69         mmin = trinity.TriVector(-1.0, 0.0, -0.866025447845459)
     70         mmax = trinity.TriVector(1.0, 0.0, 0.866025447845459)
     71         maxx = mmax.z / math.sin(math.radians(60.0))
     72         topRightX = math.cos(math.radians(60.0)) * maxx
     73         self.tileMin = Point(-maxx, mmin.z)
     74         self.tileMax = Point(maxx, mmax.z)
     75         self.hexPoints = (Point(topRightX, self.tileMax.y),
     76          Point(self.tileMax.x, 0.0),
     77          Point(topRightX, self.tileMin.y),
     78          Point(-topRightX, self.tileMin.y),
     79          Point(self.tileMin.x, 0.0),
     80          Point(-topRightX, self.tileMax.y))
     81         self.tileWidth = maxx * 2.0 * 0.75
     82         self.tileHeight = mmax.z * 2.0
     83 
     84     def GetNextGeom(self):
     85         if len(self.tilePool) > self.nextTileNum:
     86             nextTile = self.tilePool[self.nextTileNum]
     87         else:
     88             nextTile = HexGeom(self)
     89             self.tilePool.append(nextTile)
     90         self.nextTileNum += 1
     91         return nextTile
     92 
     93     def GetAdaptedTileAABB(self, tile):
     94         v = Point(tile.center[0], tile.center[2])
     95         return AABB(self.tileMin * tile.scale + v, self.tileMax * tile.scale + v)
     96 
     97     def GetAdaptedHexPoints(self, tile):
     98         newPoints = []
     99         pos = Point(tile.center[0], tile.center[2])
    100         for point in self.hexPoints:
    101             newPoint = point * tile.scale + pos
    102             newPoints.append(newPoint)
    103 
    104         return newPoints
    105 
    106     def AssignSystemsToTile(self, tile):
    107         aabb = self.GetAdaptedTileAABB(tile)
    108         systemInfos = self.systemQuadTree.Hit(aabb)
    109         if len(systemInfos) > 0:
    110             hexVertices = self.GetAdaptedHexPoints(tile)
    111             for info in systemInfos:
    112                 if self.PointWithinTile(info.point, hexVertices):
    113                     tile.systems[info.systemID] = info
    114 
    115     def PointWithinTile(self, point, vertices):
    116         numPoint = len(vertices)
    117         for i in xrange(numPoint):
    118             p0 = vertices[i]
    119             p1 = vertices[(i + 1) % numPoint]
    120             test = (point.y - p0.y) * (p1.x - p0.x) - (point.x - p0.x) * (p1.y - p0.y)
    121             if test > 0:
    122                 return False
    123 
    124         return True
    125 
    126     def GetLayout(self, sizeX):
    127         if sizeX not in self.layoutCache:
    128             layout = []
    129             startX, startY = self.aabb.min.x, self.aabb.min.y
    130             sizeY = int(math.ceil(sizeX * Y_SCALE))
    131             scale = self.aabb.Width() / (sizeX - 1) / self.tileWidth
    132             sizeY = int(math.ceil(self.aabb.Height() / (self.tileHeight * scale))) + 1
    133             height = self.tileHeight * scale
    134             width = self.tileWidth * scale
    135             offset = -height * 0.5
    136             for y in xrange(sizeY):
    137                 for x in xrange(sizeX):
    138                     tile = HexTile()
    139                     tile.scale = scale
    140                     shift = 0.0 if x % 2 == 0 else offset
    141                     tile.center = (startX + x * width, 0.0, startY + y * height + shift)
    142                     self.AssignSystemsToTile(tile)
    143                     layout.append(tile)
    144 
    145             self.layoutCache[sizeX] = layout
    146         return self.layoutCache[sizeX]
    147 
    148     def LayoutTiles(self, size, sovereigntyInfo, changeList, colorByStandings = False):
    149         changedSystems = set([ info.solarSystemID for info in changeList ])
    150         for info in self.systemInfoMap.itervalues():
    151             if info.systemID in sovereigntyInfo:
    152                 info.sovID = sovereigntyInfo[info.systemID]
    153             info.changed = info.systemID in changedSystems
    154 
    155         layout = self.GetLayout(size)
    156         self.nextTileNum = 0
    157         for i, tile in enumerate(layout):
    158             if tile.IsEmpty():
    159                 continue
    160             tile.Update(colorByStandings)
    161             hex = self.GetNextGeom()
    162             hex.SetTile(tile)
    163             hex.index = i
    164 
    165         if self.firstUnusedTileNum is not None and self.firstUnusedTileNum > self.nextTileNum:
    166             for i in xrange(self.nextTileNum, self.firstUnusedTileNum):
    167                 self.tilePool[i].Show(False)
    168 
    169         self.firstUnusedTileNum = self.nextTileNum
    170         self.currentLayout = layout
    171         self.currentSize = size
    172         if colorByStandings:
    173             self.legend = [LegendItem(0, localization.GetByLabel('UI/PeopleAndPlaces/GoodStanding'), trinity.TriColor(*COLOR_STANDINGS_GOOD), data=None), LegendItem(1, localization.GetByLabel('UI/PeopleAndPlaces/StandingNeutral'), trinity.TriColor(*COLOR_STANDINGS_NEUTRAL), data=None), LegendItem(2, localization.GetByLabel('UI/PeopleAndPlaces/BadStanding'), trinity.TriColor(*COLOR_STANDINGS_BAD), data=None)]
    174         else:
    175             legend = [ (tile.sovID, tile.color) for tile in self.currentLayout if tile.sovID is not None ]
    176             self.legend = [LegendItem(0, localization.GetByLabel('UI/Inflight/Brackets/SystemUnclaimed'), trinity.TriColor(*UNCLAIMED_TILE_COLOR), data=None), LegendItem(1, localization.GetByLabel('UI/Inflight/Brackets/SystemContested'), trinity.TriColor(*TIED_TILE_COLOR), data=None)]
    177             factions = []
    178             for sovID, color in set(legend):
    179                 name = cfg.eveowners.Get(sovID).name
    180                 factions.append(LegendItem(2, name, trinity.TriColor(*color), data=sovID))
    181 
    182             self.legend += factions
    183         self.highlightCurveSet.Play()
    184 
    185     def Enable(self, enable = True):
    186         self.rootTransform.display = enable
    187 
    188     def HighlightTiles(self, dataList, colorList):
    189         colors = [ (color.r,
    190          color.g,
    191          color.b,
    192          1.0) for color in colorList ]
    193         for geom in self.tilePool:
    194             geom.Highlight(False)
    195             if self.currentLayout[geom.index].sovID in dataList:
    196                 geom.Highlight(True)
    197                 continue
    198             c2 = self.currentLayout[geom.index].color
    199             for c1 in colors:
    200                 if abs(c1[0] - c2[0]) < 0.01 and abs(c1[1] - c2[1]) < 0.01 and abs(c1[2] - c2[2]) < 0.01:
    201                     geom.Highlight(True)
    202                     break
    203 
    204     def EnableOutlines(self, enable):
    205         numTiles = len(self.currentLayout)
    206         for geom in self.tilePool:
    207             if not geom.tile.display:
    208                 break
    209             if not enable:
    210                 geom.SetOutlines(0)
    211                 continue
    212             info = self.currentLayout[geom.index]
    213             if info.sovID is None:
    214                 geom.SetOutlines(0)
    215             else:
    216                 if geom.index % 2 == 0:
    217                     neighbors = [(32, geom.index + self.currentSize + 1),
    218                      (16, geom.index + self.currentSize),
    219                      (8, geom.index + self.currentSize - 1),
    220                      (4, geom.index - 1),
    221                      (2, geom.index - self.currentSize),
    222                      (1, geom.index + 1)]
    223                 else:
    224                     neighbors = [(32, geom.index + 1),
    225                      (16, geom.index + self.currentSize),
    226                      (8, geom.index - 1),
    227                      (4, geom.index - self.currentSize - 1),
    228                      (2, geom.index - self.currentSize),
    229                      (1, geom.index - self.currentSize + 1)]
    230                 tileIndex = 0
    231                 for bit, index in neighbors:
    232                     if 0 <= index < numTiles:
    233                         neighborTile = self.currentLayout[index]
    234                         if neighborTile.color == info.color:
    235                             continue
    236                     tileIndex += bit
    237 
    238                 geom.SetOutlines(tileIndex)
    239 
    240 
    241 class HexSystemInfo(object):
    242 
    243     def __init__(self, systemID, sovID, x, y):
    244         self.systemID = systemID
    245         self.sovID = sovID
    246         self.point = Point(x, y)
    247         self.changed = False
    248 
    249     def GetPoint(self):
    250         return self.point
    251 
    252     min = property(GetPoint)
    253     max = property(GetPoint)
    254 
    255 
    256 class HexGeom(object):
    257 
    258     def __init__(self, controller):
    259         self.controller = controller
    260         self.tile = controller.baseTile.CopyTo()
    261         parameters = self.tile.mesh.additiveAreas[0].effect.parameters
    262         self.color = [ p for p in parameters if p.name == 'DiffuseColor' ][0]
    263         self.highlight = [ p for p in parameters if p.name == 'Highlight' ][0]
    264         self.tileIndex = [ p for p in parameters if p.name == 'TileIndex' ][0]
    265         self.index = None
    266         controller.rootTransform.children.append(self.tile)
    267         self.highlightBinding = None
    268 
    269     def SetTile(self, tile):
    270         self.Show()
    271         self.SetColor(tile.color)
    272         self.tile.translation = tile.center
    273         self.tile.scaling = (tile.scale, tile.scale, tile.scale)
    274         self.Flash(tile.changed)
    275 
    276     def Show(self, enable = True):
    277         self.tile.display = enable
    278 
    279     def Highlight(self, enable = True):
    280         if enable:
    281             self.highlight.value = 1.0
    282         else:
    283             self.highlight.value = 0.0
    284 
    285     def Flash(self, enable = True):
    286         if enable and self.highlightBinding is None:
    287             binding = trinity.TriValueBinding()
    288             binding.sourceAttribute = 'currentValue'
    289             binding.destinationAttribute = 'value'
    290             binding.scale = 1.0
    291             binding.sourceObject = self.controller.highlightCurve
    292             binding.destinationObject = self.highlight
    293             self.highlightBinding = binding
    294         if enable:
    295             if self.highlightBinding not in self.controller.highlightCurveSet.bindings:
    296                 self.controller.highlightCurveSet.bindings.append(self.highlightBinding)
    297         else:
    298             if self.highlightBinding in self.controller.highlightCurveSet.bindings:
    299                 self.controller.highlightCurveSet.bindings.remove(self.highlightBinding)
    300             self.highlight.value = 0.0
    301 
    302     def SetOutlines(self, index):
    303         self.tileIndex.value = index
    304 
    305     def SetColor(self, color):
    306         self.color.value = color
    307 
    308 
    309 class HexTile(object):
    310 
    311     def __init__(self):
    312         self.center = None
    313         self.color = None
    314         self.sovID = None
    315         self.systems = {}
    316         self.changed = False
    317 
    318     def IsEmpty(self):
    319         return len(self.systems) == 0
    320 
    321     def Update(self, colorByStandings = False):
    322         alliances = {}
    323         self.changed = False
    324         starmap = sm.GetService('starmap')
    325         for info in self.systems.itervalues():
    326             if info.changed:
    327                 self.changed = True
    328             if info.sovID is None:
    329                 continue
    330             if info.sovID not in alliances:
    331                 alliances[info.sovID] = 0
    332             alliances[info.sovID] += 1
    333 
    334         histogram = [ (count, sovID) for sovID, count in alliances.iteritems() if sovID is not None ]
    335         histogram.sort()
    336         if len(histogram) == 0:
    337             self.sovID = None
    338             self.color = UNCLAIMED_TILE_COLOR
    339         elif len(histogram) == 1 or histogram[-1][0] != histogram[-2][0]:
    340             self.sovID = histogram[-1][1]
    341             if colorByStandings:
    342                 c = starmap.GetColorByStandings(self.sovID)
    343                 color = (c[0],
    344                  c[1],
    345                  c[2],
    346                  1.0)
    347             else:
    348                 c = starmap.GetFactionOrAllianceColor(self.sovID)
    349                 color = (c.r,
    350                  c.g,
    351                  c.b,
    352                  1.0)
    353             self.color = color
    354         elif histogram[-1][0] == histogram[-2][0]:
    355             self.sovID == None
    356             self.color = TIED_TILE_COLOR
    357         else:
    358             self.sovID == None
    359             self.color = UNCLAIMED_TILE_COLOR
    360 
    361     def __repr__(self):
    362         return 'HexTile(sov=%s center=%s color=%s systems=%s)' % (str(self.sovID),
    363          str(self.center),
    364          str(self.color),
    365          str([ i for i in self.systems ]))
    366 
    367 
    368 class AABB(object):
    369 
    370     def __init__(self, minPoint = None, maxPoint = None):
    371         if minPoint is None:
    372             self.min = Point()
    373         else:
    374             self.min = minPoint
    375         if maxPoint is None:
    376             self.max = Point()
    377         else:
    378             self.max = maxPoint
    379 
    380     def Width(self):
    381         return self.max.x - self.min.x
    382 
    383     def Height(self):
    384         return self.max.y - self.min.y
    385 
    386     def IncludePoint(self, point):
    387         self.min.x = min(self.min.x, point.x)
    388         self.min.y = min(self.min.y, point.y)
    389         self.max.x = max(self.max.x, point.x)
    390         self.max.y = max(self.max.y, point.y)
    391 
    392     def Center(self):
    393         return (self.min + self.max) * 0.5
    394 
    395     def __repr__(self):
    396         return '(%f, %f, %f, %f)' % (self.min.x,
    397          self.min.y,
    398          self.max.x,
    399          self.max.y)
    400 
    401 
    402 class Point(object):
    403 
    404     def __init__(self, x = 0, y = 0):
    405         self.x = x
    406         self.y = y
    407 
    408     def __mul__(self, scalar):
    409         return Point(self.x * scalar, self.y * scalar)
    410 
    411     def __add__(self, other):
    412         return Point(self.x + other.x, self.y + other.y)
    413 
    414     def dot(self, other):
    415         return self.x * other.x + self.y * other.y
    416 
    417     def __repr__(self):
    418         return '(%f, %f)' % (self.x, self.y)
    419 
    420 
    421 class QuadTree(object):
    422 
    423     def __init__(self, items, depth = 8, aabb = None):
    424         self.nw = self.ne = self.se = self.sw = None
    425         depth -= 1
    426         if depth == 0:
    427             self.items = items
    428             return
    429         if aabb is None:
    430             aabb = AABB()
    431             aabb.min.x = min((item.min.x for item in items))
    432             aabb.min.y = min((item.min.y for item in items))
    433             aabb.max.x = max((item.max.x for item in items))
    434             aabb.max.y = max((item.max.y for item in items))
    435         self.center = center = aabb.Center()
    436         self.items = []
    437         nw_items = []
    438         ne_items = []
    439         se_items = []
    440         sw_items = []
    441         for item in items:
    442             in_nw = item.min.x <= center.x and item.min.y <= center.y
    443             in_sw = item.min.x <= center.x and item.max.y >= center.y
    444             in_ne = item.max.x >= center.x and item.min.y <= center.y
    445             in_se = item.max.x >= center.x and item.max.y >= center.y
    446             if in_nw and in_ne and in_se and in_sw:
    447                 self.items.append(item)
    448             else:
    449                 if in_nw:
    450                     nw_items.append(item)
    451                 if in_ne:
    452                     ne_items.append(item)
    453                 if in_se:
    454                     se_items.append(item)
    455                 if in_sw:
    456                     sw_items.append(item)
    457 
    458         if nw_items:
    459             self.nw = QuadTree(nw_items, depth, AABB(Point(aabb.min.x, aabb.min.y), Point(center.x, center.y)))
    460         if ne_items:
    461             self.ne = QuadTree(ne_items, depth, AABB(Point(center.x, aabb.min.y), Point(aabb.max.x, center.y)))
    462         if se_items:
    463             self.se = QuadTree(se_items, depth, AABB(Point(center.x, center.y), Point(aabb.max.x, aabb.max.y)))
    464         if sw_items:
    465             self.sw = QuadTree(sw_items, depth, AABB(Point(aabb.min.x, center.y), Point(center.x, aabb.max.y)))
    466 
    467     def Overlaps(self, rect, item):
    468         return rect.max.x >= item.min.x and rect.min.x <= item.max.x and rect.max.y >= item.min.y and rect.min.y <= item.max.y
    469 
    470     def Hit(self, rect):
    471         hits = set((item for item in self.items if self.Overlaps(rect, item)))
    472         if self.nw and rect.min.x <= self.center.x and rect.min.y <= self.center.y:
    473             hits |= self.nw.Hit(rect)
    474         if self.sw and rect.min.x <= self.center.x and rect.max.y >= self.center.y:
    475             hits |= self.sw.Hit(rect)
    476         if self.ne and rect.max.x >= self.center.x and rect.min.y <= self.center.y:
    477             hits |= self.ne.Hit(rect)
    478         if self.se and rect.max.x >= self.center.x and rect.max.y >= self.center.y:
    479             hits |= self.se.Hit(rect)
    480         return hits