hexMap.py (31268B)
1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\eve\client\script\ui\shared\mapView\hexagonal\hexMap.py 2 from __future__ import print_function 3 import math 4 import logging 5 from carbon.common.script.util.timerstuff import AutoTimer 6 from carbonui.primitives.base import ScaleDpi, ReverseScaleDpi 7 from carbonui.primitives.fill import Fill 8 from carbonui.primitives.sprite import Sprite 9 from carbonui.primitives.vectorlinetrace import VectorLineTrace 10 from carbonui.primitives.container import Container 11 from eve.client.script.ui.control.eveLabel import EveLabelSmall 12 from eve.client.script.ui.shared.mapView.hexagonal import hexUtil 13 import carbonui.const as uiconst 14 import geo2 15 from eve.client.script.ui.shared.mapView.hexagonal.hexMapLine import HexMapLine 16 from evePathfinder.core import EvePathfinderCore 17 from evePathfinder.eveMapWrapper import EveMapWrapper 18 from evePathfinder.pathfinder import ClientPathfinder 19 from evePathfinder.stateinterface import StandardPathfinderInterface 20 import trinity 21 import pyEvePathfinder 22 from eve.common.script.sys.idCheckers import IsConstellation, IsRegion 23 from eve.client.script.ui.shared.mapView.hexagonal.hexMapUtil import GetHexMapSizeForObject, GetPlotDataForObject, PrepareCombinedMap 24 from carbonui.uicore import uicore 25 logger = logging.getLogger(__name__) 26 27 class HexMap(Container): 28 default_align = uiconst.TOPLEFT 29 default_state = uiconst.UI_PICKCHILDREN 30 isChild = False 31 isCombinedMap = False 32 isFlatTop = True 33 editMode = False 34 parentMap = None 35 gridSprite = None 36 _morphGlobalScaling = 0.0 37 _morphGlobalScalingValues = None 38 globalScaling = 1.0 39 localScaling = 1.0 40 mapsize = 3 41 setMapSize = None 42 objectID = None 43 objectData = None 44 objectByID = None 45 connectionsByID = None 46 hexGridConnectionsByID = {} 47 markersByID = None 48 colorsInverted = False 49 slotsByPosition = None 50 hexGridSize = 48.0 51 sizeUnscaled = None 52 outline = None 53 54 def ApplyAttributes(self, attributes): 55 self.parentMap = attributes.parentMap 56 self.isChild = bool(self.parentMap) 57 self.editMode = attributes.editMode 58 self.hexGridSize = attributes.hexGridSize or self.hexGridSize 59 self.setMapSize = attributes.setMapSize 60 if self.parentMap: 61 self.colorsInverted = not self.parentMap.colorsInverted 62 Container.ApplyAttributes(self, attributes) 63 if self.colorsInverted: 64 self.mainColor = (0, 0, 0, 1) 65 self.blockColor = (0.75, 0.75, 0.75, 1) 66 else: 67 self.mainColor = (0.75, 0.75, 0.75, 1) 68 self.blockColor = (0, 0, 0, 1) 69 Fill(parent=self, align=uiconst.CENTER, pos=(0, 0, 10, 2), color=(1, 0, 0, 1)) 70 Fill(parent=self, align=uiconst.CENTER, pos=(0, 0, 2, 10), color=(1, 0, 0, 1)) 71 72 def Close(self, *args): 73 Container.Close(self, *args) 74 self.parentMap = None 75 76 @apply 77 def morphGlobalScaling(): 78 79 def fget(self): 80 return self._morphGlobalScaling 81 82 def fset(self, value): 83 if value != self._morphGlobalScaling: 84 self._morphGlobalScaling = value 85 self.globalScaling = value 86 w = self.sizeUnscaled[0] * value 87 h = self.sizeUnscaled[1] * value 88 if self._morphGlobalScalingValues: 89 preMouse, preAbs, parentAbsolute = self._morphGlobalScalingValues 90 pl, pt, pw, ph = preAbs 91 proportionalX = (preMouse[0] - pl) / float(pw) 92 proportionalY = (preMouse[1] - pt) / float(ph) 93 left = preMouse[0] - parentAbsolute[0] - proportionalX * w 94 top = preMouse[1] - parentAbsolute[1] - proportionalY * h 95 else: 96 left = self.left 97 top = self.top 98 self.pos = (left, 99 top, 100 w, 101 h) 102 self.TraverseHexMap() 103 104 return property(**locals()) 105 106 def UpdateChildren(self): 107 pass 108 109 def TraverseHexMap(self, viewportRect = None, scaling = None): 110 if not self.parent: 111 return 112 if scaling is None: 113 scaling = self.globalScaling 114 self.globalScaling = scaling 115 if self.objectByID: 116 if viewportRect is None: 117 viewportRect = (self.left, 118 self.top, 119 ReverseScaleDpi(self.parent.displayWidth), 120 ReverseScaleDpi(self.parent.displayHeight)) 121 else: 122 vX, vY, vW, vH = viewportRect 123 viewportRect = (vX + self.left, 124 vY + self.top, 125 vW, 126 vH) 127 for objectID, hexCell in self.objectByID.iteritems(): 128 hexCell.TraverseHexCell(viewportRect, self.globalScaling * self.localScaling) 129 130 self.UpdateJumpLines() 131 132 def UpdateAlignment(self, budgetLeft = 0, budgetTop = 0, budgetWidth = 0, budgetHeight = 0, updateChildrenOnly = False): 133 ret = Container.UpdateAlignment(self, budgetLeft, budgetTop, budgetWidth, budgetHeight, updateChildrenOnly) 134 return ret 135 136 @apply 137 def displayRect(): 138 fget = Container.displayRect.fget 139 140 def fset(self, value): 141 pW, pH = self._displayWidth, self._displayHeight 142 displayX, displayY, displayWidth, displayHeight = value 143 self._displayX = int(round(displayX)) 144 self._displayY = int(round(displayY)) 145 self._displayWidth = int(round(displayX + displayWidth)) - self._displayX 146 self._displayHeight = int(round(displayY + displayHeight)) - self._displayY 147 ro = self.renderObject 148 if ro: 149 ro.displayX = self._displayX 150 ro.displayY = self._displayY 151 ro.displayWidth = self._displayWidth 152 ro.displayHeight = self._displayHeight 153 if self._displayWidth != pW or self._displayHeight != pH: 154 if self._backgroundlist and len(self.background): 155 self.UpdateBackgrounds() 156 if self.outline: 157 self.DrawOutline() 158 if self.gridSprite: 159 baseCellSize = hexUtil.hex_slot_size(self.isFlatTop, self.hexGridSize) 160 tileTextureWidth = 288.0 161 tileTextureHeight = 333.0 162 textureScaling = tileTextureHeight / baseCellSize[1] 163 self.gridSprite.rectSecondary = ((self.sizeUnscaled[0] * 2 % (self.hexGridSize * 6) - self.hexGridSize) * textureScaling + self.hexGridSize * textureScaling * 0.75, 164 0.0, 165 tileTextureWidth / self.globalScaling / self.localScaling * textureScaling * 0.5, 166 tileTextureHeight / self.globalScaling / self.localScaling * textureScaling * 0.5) 167 168 return property(**locals()) 169 170 def FlushMap(self): 171 if self.objectByID: 172 for each in self.objectByID.values(): 173 each.Close() 174 175 if self.connectionsByID: 176 for each in self.connectionsByID.values(): 177 each.Close() 178 179 if self.hexGridConnectionsByID: 180 for line1, line2 in self.hexGridConnectionsByID.values(): 181 line1.Close() 182 line2.Close() 183 184 if self.markersByID: 185 for each in self.markersByID.values(): 186 each.Close() 187 188 self.objectByID = {} 189 self.connectionsByID = {} 190 self.hexGridConnectionsByID = {} 191 self.markersByID = {} 192 self.slotsByPosition = {} 193 194 def GetVectorBetweenObjectsOnSameLevel(self, fromObjectID, toObjectID, parentObjectID, i = 0): 195 plotData = GetPlotDataForObject(parentObjectID, ignoreFixed=False) 196 if fromObjectID in plotData: 197 column1, row1 = plotData[fromObjectID] 198 fromPos = hexUtil.hex_slot_center_position(column1, row1, self.isFlatTop, self.hexGridSize) 199 elif self.parentMap and fromObjectID in self.parentMap.markersByID: 200 fromPos = self.parentMap.markersByID[fromObjectID].unscaledPosition 201 else: 202 return 203 if toObjectID in plotData: 204 column2, row2 = plotData[toObjectID] 205 toPos = hexUtil.hex_slot_center_position(column2, row2, self.isFlatTop, self.hexGridSize) 206 elif self.parentMap and toObjectID in self.parentMap.markersByID: 207 toPos = self.parentMap.markersByID[toObjectID].unscaledPosition 208 else: 209 return 210 diffVec = geo2.Vec2Subtract(toPos, fromPos) 211 diffVec = geo2.Vec2Scale(diffVec, 100000000000.0) 212 cornerPoints = [] 213 for i in xrange(6): 214 if self.isFlatTop: 215 outlineRad = self.width * 0.5 216 angle = 2.0 * math.pi / 6.0 * i 217 else: 218 outlineRad = self.height * 0.5 219 angle = 2.0 * math.pi / 6.0 * (i + 0.5) 220 x_i = outlineRad * math.cos(angle) 221 y_i = outlineRad * math.sin(angle) 222 cornerPoints.append((x_i, y_i)) 223 224 for i in xrange(6): 225 p1 = cornerPoints[i] 226 p2 = cornerPoints[i - 1] 227 crossPoint = hexUtil.intersect_line_segments((p1, p2), ((0.0, 0.0), diffVec)) 228 if crossPoint: 229 return crossPoint 230 231 def LoadExitPoints(self, exitData, parentID): 232 for exitID in exitData: 233 if self.objectID not in exitID: 234 continue 235 a, b = exitID 236 if a == self.objectID: 237 toID = b 238 else: 239 toID = a 240 crossPoint = self.GetVectorBetweenObjectsOnSameLevel(self.objectID, toID, parentID) 241 if IsRegion(toID): 242 color = (1, 0, 0, 1) 243 elif IsConstellation(toID): 244 color = (0, 1, 0, 1) 245 else: 246 color = (0, 0, 1, 1) 247 if crossPoint: 248 marker = self.AddMarker(toID, crossPoint[0], crossPoint[1], color=color, size=8) 249 marker.state = uiconst.UI_NORMAL 250 marker.hint = '%s' % toID 251 252 def LoadMapData(self, objectID, loadLayout = True, ignoreFixedLayout = None, loadCombined = False): 253 if objectID not in uicore.mapObjectDataByID: 254 return 255 loadCombined = loadCombined or settings.user.ui.Get('mapLoadCombined', 0) 256 if loadCombined and IsRegion(objectID): 257 self.isCombinedMap = True 258 layoutData, connectionData, exitConnectionData, size = PrepareCombinedMap(objectID) 259 objectData = layoutData.keys() 260 plotData = layoutData 261 else: 262 self.isCombinedMap = False 263 objectData = uicore.mapObjectDataByID[objectID] 264 connectionData = uicore.mapConnectionDataByID.get(objectID, []) 265 plotData = GetPlotDataForObject(objectID, ignoreFixedLayout) 266 if plotData: 267 size = GetHexMapSizeForObject(objectID) 268 else: 269 size = 10 270 exitConnectionData = [] 271 self.objectID = objectID 272 self.exitConnectionData = exitConnectionData 273 self.FlushMap() 274 from eve.client.script.ui.shared.mapView.hexagonal.hexCell import HexCell 275 for childObjectID in objectData: 276 hexCell = HexCell(parent=self, hexGrid=self, hexSize=self.hexGridSize * 0.5, hexGridSize=self.hexGridSize, opacity=1.0, state=uiconst.UI_NORMAL, isFlatTop=self.isFlatTop, editMode=self.editMode, idx=0) 277 hexCell.OnMouseWheelCallback = self.OnMouseWheel 278 hexCell.OnDragCallback = self.OnHexCellDrag 279 hexCell.OnDragEndCallback = self.OnHexCellEndDrag 280 hexCell.renderObject.pickRadius = -1 281 hexCell.objectID = childObjectID 282 self.objectByID[childObjectID] = hexCell 283 284 if loadLayout: 285 if plotData: 286 try: 287 for _objectID in objectData: 288 hexCell = self.objectByID[_objectID] 289 col, row = plotData[_objectID] 290 hexCell.MoveToCR(col, row) 291 292 except KeyError as e: 293 if ignoreFixedLayout is None: 294 return self.LoadMapData(objectID, ignoreFixedLayout=True) 295 return 296 297 else: 298 return 299 self.SetMapSize(size) 300 loadExitpoints = settings.user.ui.Get('mapDebugShowExitPoints', 0) 301 if loadExitpoints: 302 self.DrawOutline() 303 parentID = uicore.mapObjectParentByID.get(self.objectID, None) 304 if parentID and parentID in uicore.mapConnectionDataByID: 305 parentConnections = uicore.mapConnectionDataByID[parentID] 306 self.LoadExitPoints(parentConnections, parentID) 307 loadHexLines = settings.user.ui.Get('mapDebugLoadHexLines', 0) 308 self.LoadConnections(connectionData, createHexLines=loadHexLines) 309 self.LoadConnections(self.exitConnectionData, createHexLines=loadHexLines) 310 if loadHexLines: 311 self.UpdateJumpLines(refreshLayout=True) 312 if not self.isChild: 313 self.TraverseHexMap() 314 315 def LoadConnections(self, connectionData, createHexLines = False): 316 for fromID, toID in connectionData: 317 if (fromID, toID) in self.connectionsByID or (toID, fromID) in self.connectionsByID: 318 continue 319 self.connectionsByID[fromID, toID] = VectorLineTrace(parent=self) 320 if createHexLines and fromID in self.objectByID and toID in self.objectByID: 321 self.hexGridConnectionsByID[fromID, toID] = (HexMapLine(parent=self), HexMapLine(parent=self)) 322 323 def GetCurrentLayout(self): 324 layoutByObjectID = {} 325 for objectID, uiObject in self.objectByID.iteritems(): 326 layoutByObjectID[objectID] = uiObject.GetGridPosition() 327 328 return layoutByObjectID 329 330 def GetOccupiedSlots(self): 331 occupied = [] 332 for objectID, uiObject in self.objectByID.iteritems(): 333 if uiObject.GetGridPosition(): 334 occupied.append(uiObject.GetGridPosition()) 335 336 return occupied 337 338 def GetAxialCoordinateFromMousePosition(self): 339 absLeft, absTop, absWidth, absHeight = self.GetAbsolute() 340 pickX, pickY = uicore.uilib.x - absLeft - absWidth / 2, uicore.uilib.y - absTop - absHeight / 2 341 return self.GetAxialCoordinateFromUIPosition(pickX, pickY) 342 343 def GetAxialCoordinateFromUIPosition(self, x, y): 344 px_hx = hexUtil.pixel_to_hex(x, y, self.hexGridSize, isFlatTop=self.isFlatTop) 345 ax_cu = hexUtil.axial_to_cube_coordinate(*px_hx) 346 ax_cu_rounded = hexUtil.hex_round(*ax_cu) 347 if self.isFlatTop: 348 cu_ax = hexUtil.cube_to_odd_q_axial_coordinate(*ax_cu_rounded) 349 else: 350 cu_ax = hexUtil.cube_to_odd_r_axial_coordinate(*ax_cu_rounded) 351 return cu_ax 352 353 def OnHexCellDrag(self, *args): 354 self.TraverseHexMap() 355 356 def OnHexCellEndDrag(self, hexCell, *args): 357 if self.globalScaling != 1.0: 358 return 359 CR = self.GetAxialCoordinateFromMousePosition() 360 hexCell.MoveToCR(*CR) 361 self.UpdateJumpLines(refreshLayout=True) 362 self.TraverseHexMap() 363 if not self.isCombinedMap: 364 positions = {} 365 for objectID, hexCell in self.objectByID.iteritems(): 366 positions[objectID] = hexCell.GetGridPosition() 367 368 handmade = settings.user.ui.Get('mapHexLayout', {}) 369 handmade[self.objectID] = positions 370 settings.user.ui.Set('mapHexLayout', handmade) 371 372 def OnMouseWheel(self, dz, *args): 373 if self.isChild: 374 return 375 preAbsolute = self.GetAbsolute() 376 parentAbsolute = self.parent.GetAbsolute() 377 preMouse = (uicore.uilib.x, uicore.uilib.y) 378 if dz < 0: 379 globalScaling = min(8192.0, self.globalScaling * 4) 380 else: 381 globalScaling = max(0.125, self.globalScaling / 4) 382 self._morphGlobalScalingValues = (preMouse, preAbsolute, parentAbsolute) 383 uicore.animations.MorphScalar(self, 'morphGlobalScaling', startVal=self.globalScaling, endVal=globalScaling, duration=0.5) 384 385 def OnMouseDown(self, mouseButton, *args): 386 if self.isChild: 387 return 388 if self.editMode: 389 ctrl = uicore.uilib.Key(uiconst.VK_CONTROL) 390 if ctrl: 391 positions = {} 392 for objectID, hexCell in self.objectByID.iteritems(): 393 positions[objectID] = hexCell.GetGridPosition() 394 395 print(self.objectID, ':', positions) 396 print('----') 397 print(sorted(positions.values())) 398 self.initDragData = (self.left, 399 self.top, 400 uicore.uilib.x, 401 uicore.uilib.y) 402 self.dragThread = AutoTimer(1, self.DragMap) 403 404 def OnMouseUp(self, mouseButton, *args): 405 if self.isChild: 406 return 407 408 def DragMap(self): 409 if not uicore.uilib.leftbtn: 410 self.dragThread = None 411 initLeft, initTop, initMouseX, initMouseY = self.initDragData 412 dX = initMouseX - uicore.uilib.x 413 dY = initMouseY - uicore.uilib.y 414 parentWidth = ScaleDpi(self.parent.displayWidth) 415 parentHeight = ScaleDpi(self.parent.displayHeight) 416 w, h = self.width, self.height 417 self.left = min(parentWidth / 2, max(parentWidth / 2 - w, initLeft - dX)) 418 self.top = min(parentHeight / 2, max(parentHeight / 2 - h, initTop - dY)) 419 self.TraverseHexMap() 420 421 def UpdateJumpLines(self, refreshLayout = False): 422 if self.markersByID: 423 for markerID, marker in self.markersByID.iteritems(): 424 marker.left = marker.unscaledPosition[0] * self.globalScaling * self.localScaling 425 marker.top = marker.unscaledPosition[1] * self.globalScaling * self.localScaling 426 427 if self.connectionsByID: 428 for (fromID, toID), line in self.connectionsByID.iteritems(): 429 if fromID < 1000: 430 continue 431 line.Flush() 432 isExit = False 433 lineColor = self.mainColor 434 fromObj = self.objectByID.get(fromID, None) 435 if fromObj is None and fromID in self.markersByID: 436 fromObj = self.markersByID.get(fromID, None) 437 isExit = True 438 lineColor = (1, 0, 0, 1) 439 toObj = self.objectByID.get(toID, None) 440 if toObj is None and toID in self.markersByID: 441 toObj = self.markersByID.get(toID, None) 442 isExit = True 443 lineColor = (1, 0, 0, 1) 444 if fromObj and toObj: 445 p1, p2 = self.ApplyLineMargin((fromObj.left + self.width / 2.0, fromObj.top + self.height / 2.0), (toObj.left + self.width / 2.0, toObj.top + self.height / 2.0), fromObj.hexSize * self.globalScaling * self.localScaling, toObj.hexSize * self.globalScaling * self.localScaling) 446 if p1 and p2: 447 line.AddPoint(p1, getattr(line, 'lineColor', lineColor)) 448 line.AddPoint(p2, getattr(line, 'lineColor', lineColor)) 449 450 if self.hexGridConnectionsByID: 451 if refreshLayout: 452 self.RefreshHexGridConnectionsLayout() 453 for (fromID, toID), lines in self.hexGridConnectionsByID.iteritems(): 454 line, line2 = lines 455 line.Flush() 456 line2.Flush() 457 if line.positionsUnscaled: 458 line.display = True 459 line.display = True 460 line.lineWidth = line.lineWidthUnscaled * self.globalScaling * self.localScaling 461 line2.lineWidth = line.lineWidth * 1.6 462 line.AddPoints(line.positionsUnscaled, scaling=self.globalScaling * self.localScaling) 463 line2.AddPoints(line.positionsUnscaled, scaling=self.globalScaling * self.localScaling) 464 else: 465 line.display = False 466 line.display = False 467 468 def ApplyLineMargin(self, p1, p2, radius1, radius2): 469 v = geo2.Vec2Subtract(p1, p2) 470 vn = geo2.Vec2Normalize(v) 471 l = geo2.Vec2Length(v) 472 if not l: 473 return (None, None) 474 s = (radius1 + radius2) / l 475 mp1 = geo2.Vec2Subtract(p1, geo2.Vec2Scale(vn, radius1)) 476 mp2 = geo2.Vec2Add(p2, geo2.Vec2Scale(vn, radius2)) 477 return (mp1, mp2) 478 479 def SetMapSize(self, size): 480 size = self.setMapSize or size 481 size += size & 1 482 self.mapsize = size 483 baseSize = hexUtil.hex_slot_size(self.isFlatTop, self.hexGridSize * size) 484 if baseSize != (self.width, self.height): 485 self.sizeUnscaled = baseSize 486 self.morphGlobalScaling = 0.0 487 self.morphGlobalScaling = 1.0 488 489 def DrawOutline(self): 490 if self.outline: 491 outline = self.outline 492 else: 493 outline = VectorLineTrace(parent=self, lineWidth=2) 494 outline.isLoop = True 495 self.outline = outline 496 outline.Flush() 497 for i in xrange(6): 498 if self.isFlatTop: 499 outlineRad = self.displayWidth * 0.5 500 angle = 2.0 * math.pi / 6.0 * i 501 else: 502 outlineRad = self.displayHeight * 0.5 503 angle = 2.0 * math.pi / 6.0 * (i + 0.5) 504 x_i = ReverseScaleDpi(self.displayWidth * 0.5 + outlineRad * math.cos(angle)) 505 y_i = ReverseScaleDpi(self.displayHeight * 0.5 + outlineRad * math.sin(angle)) 506 outline.AddPoint((x_i, y_i), (1, 0, 0, 0.8)) 507 508 def AddMarker(self, markerID, x, y, color = (1, 1, 1, 0.5), size = 10): 509 marker = Container(parent=self, align=uiconst.CENTER, pos=(x, 510 y, 511 size, 512 size), idx=0) 513 marker.markerID = markerID 514 self.markersByID[markerID] = marker 515 marker.unscaledPosition = (x, y) 516 marker.hexSize = 1 517 Fill(bgParent=marker, color=color) 518 return marker 519 520 def PreparePathFinder(self): 521 xStep, yStep = hexUtil.hex_slot_size(self.isFlatTop, self.hexGridSize / 2.0) 522 xRange = int(self.width / xStep) 523 yRange = int(self.height / yStep) 524 xRange = xRange - xRange % 3 + 3 525 yRange = yRange - yRange % 3 + 3 526 pathMap = EveMapWrapper() 527 pathMap.add_region(-1) 528 pathMap.add_constellation(-2, -1) 529 self.pathMap = pathMap 530 pathfinderCore = pathMap.create_core() 531 pathfinderState = HexMapPathfinderInterface() 532 self.avoidPathFinderState = HexMapPathfinderInterface() 533 534 def ConvertPathFinderIDMock(ret): 535 return ret 536 537 self.pathfinderHandler = ClientPathfinder(pathfinderCore, pathfinderState, self.avoidPathFinderState, ConvertPathFinderIDMock, ConvertPathFinderIDMock) 538 self.avoidPathFinderState.blockedSpots = [] 539 self.avoidPathFinderState.usedSpots = [] 540 if getattr(self, 'pathFindingPairs', None): 541 for i in xrange(yRange * xRange): 542 pathMap.add_solar_system(i, -2, -1) 543 544 self.AddToPathFinding(self.pathFindingPairs) 545 return 546 self.column_row_to_pathfinder = column_row_to_pathfinder = {} 547 self.pathfinder_to_xy = pathfinder_to_xy = [] 548 self.pathfinderIndexGrid = indexGrid = [] 549 self.pathFindingPairs = pathFindingPairs = set() 550 self.pathFindingPairSequence = pathFindingPairSequence = {} 551 showDebug = not self.isChild and settings.user.ui.Get('mapDebugSubdivision', 0) 552 self.pathfinderSize = (xRange, yRange) 553 i = 0 554 for y in xrange(yRange): 555 row = [] 556 indexGrid.append(row) 557 yOdd = y & 1 558 for x in xrange(xRange): 559 row.append(i) 560 posX = x * xStep 561 posY = y * yStep 562 if y % 2: 563 posX += xStep / 2 564 isPrimary = not (x - yOdd + self.mapsize / 2 % 3) % 3 565 if isPrimary: 566 px_hx = hexUtil.pixel_to_hex(posX - self.width / 2, posY - self.height / 2, self.hexGridSize, isFlatTop=self.isFlatTop) 567 ax_cu = hexUtil.axial_to_cube_coordinate(*px_hx) 568 ax_cu_rounded = hexUtil.hex_round(*ax_cu) 569 cu_ax = hexUtil.cube_to_odd_q_axial_coordinate(*ax_cu_rounded) 570 column_row_to_pathfinder[cu_ax] = i 571 for cMod, rMod in ((-1, 0), (-1 + yOdd, -1), (yOdd, -1)): 572 cIndex = x + cMod 573 rIndex = y + rMod 574 if rIndex >= 0 and cIndex >= 0: 575 try: 576 pathFindingPairs.add((i, indexGrid[rIndex][cIndex])) 577 except IndexError: 578 pass 579 580 if showDebug: 581 if isPrimary: 582 textColor = (1, 0, 0, 1) 583 else: 584 textColor = (0, 1, 0, 1) 585 EveLabelSmall(parent=self, text=str(i), left=posX - self.width / 2, top=posY - self.height / 2, align=uiconst.CENTER, color=textColor) 586 pathfinder_to_xy.append((posX, posY)) 587 pathMap.add_solar_system(i, -2, -1) 588 i += 1 589 590 self.AddToPathFinding(self.pathFindingPairs, showDebug) 591 592 def RefreshHexGridConnectionsLayout(self): 593 self.PreparePathFinder() 594 busyPrimary = [ each.GetGridPosition() for objectID, each in self.objectByID.iteritems() ] 595 self.avoidPathFinderState.usedSpots = [] 596 if self.hexGridConnectionsByID: 597 connectionIDs = self.hexGridConnectionsByID.keys() 598 unresolved = self.UpdateHexGridConnections(connectionIDs, busyPrimary) 599 if unresolved: 600 self.LoadExtraPrimaryConnections() 601 unresolved = self.UpdateHexGridConnections(unresolved[:], busyPrimary) 602 if unresolved: 603 print('STILL UNRESOLVED !!!!', len(unresolved)) 604 605 def LoadExtraPrimaryConnections(self): 606 print('LoadExtraPrimaryConnections') 607 xRange, yRange = self.pathfinderSize 608 indexGrid = self.pathfinderIndexGrid 609 pathFindingPairs = set() 610 showDebug = False 611 i = 0 612 for y in xrange(yRange): 613 yOdd = y & 1 614 for x in xrange(xRange): 615 isPrimary = not (x - yOdd + self.mapsize / 2 % 3) % 3 616 if isPrimary: 617 for cMod, rMod in ((-2 + yOdd, -1), (0, -2), (1 + yOdd, -1)): 618 cIndex = x + cMod 619 rIndex = y + rMod 620 if rIndex >= 0 and cIndex >= 0: 621 try: 622 fromIndex, toIndex = i, indexGrid[rIndex][cIndex] 623 pathFindingPairs.add((fromIndex, toIndex)) 624 fromIndex, toIndex = sorted((fromIndex, toIndex)) 625 if rMod == 0: 626 self.pathFindingPairSequence[fromIndex, toIndex] = range(fromIndex, toIndex + 1) 627 except IndexError: 628 pass 629 630 i += 1 631 632 self.AddToPathFinding(pathFindingPairs, showDebug, groupID=-1) 633 634 def AddToPathFinding(self, pathFindingPairs, showDebug = False, groupID = 0, debugColor = None): 635 pathfinder_to_xy = self.pathfinder_to_xy 636 for _p1, _p2 in pathFindingPairs: 637 self.pathMap.add_jump(_p1, _p2, groupID) 638 self.pathMap.add_jump(_p2, _p1, groupID) 639 if (showDebug or debugColor) and not self.isChild: 640 self._DebugDrawLine(pathfinder_to_xy[_p1], pathfinder_to_xy[_p2], debugColor or (1, 0, 0, 0.5)) 641 642 self.pathMap.finalize() 643 644 def UpdateHexGridConnections(self, connectionIDs, busyPrimary, lineColor = None): 645 column_row_to_pathfinder = self.column_row_to_pathfinder 646 pathfinder_to_xy = self.pathfinder_to_xy 647 unresolvedConnections = [] 648 for fromID, toID in connectionIDs: 649 fromObj = self.objectByID.get(fromID, None) or self.markersByID.get(fromID, None) 650 toObj = self.objectByID.get(toID, None) or self.markersByID.get(toID, None) 651 if not hasattr(fromObj, 'GetGridPosition'): 652 continue 653 if not hasattr(toObj, 'GetGridPosition'): 654 continue 655 cr1 = fromObj.GetGridPosition() 656 cr2 = toObj.GetGridPosition() 657 self.avoidPathFinderState.blockedSpots = [ column_row_to_pathfinder[cr] for cr in busyPrimary if cr not in (cr1, cr2) ] 658 line, line2 = self.hexGridConnectionsByID[fromID, toID] 659 path = self.pathfinderHandler.GetAutopilotPathBetween(column_row_to_pathfinder[cr1], column_row_to_pathfinder[cr2]) 660 if not path: 661 unresolvedConnections.append((fromID, toID)) 662 line.positionsUnscaled = None 663 line2.positionsUnscaled = None 664 if (fromID, toID) in self.connectionsByID: 665 self.connectionsByID[fromID, toID].lineColor = (1, 0, 0, 1) 666 continue 667 if (fromID, toID) in self.connectionsByID: 668 self.connectionsByID[fromID, toID].lineColor = (0, 0, 0, 0) 669 line.lineColor = lineColor or self.mainColor 670 line.lineWidthUnscaled = 4.0 671 line2.lineColor = self.blockColor 672 line.positionsUnscaled = [] 673 for i, pointIndex in enumerate(path): 674 self.avoidPathFinderState.usedSpots.append(pointIndex) 675 try: 676 nextPointIndex = path[i + 1] 677 jumpKey1 = (pointIndex, nextPointIndex) 678 jumpKey2 = (nextPointIndex, pointIndex) 679 occupy = self.pathFindingPairSequence.get(jumpKey1, None) or self.pathFindingPairSequence.get(jumpKey2, None) 680 if occupy: 681 for each in occupy: 682 if each not in self.avoidPathFinderState.usedSpots: 683 self.avoidPathFinderState.usedSpots.append(each) 684 685 except IndexError: 686 pass 687 688 pos = pathfinder_to_xy[pointIndex] 689 line.positionsUnscaled.append(pos) 690 691 return unresolvedConnections 692 693 def _DebugDrawLine(self, p1, p2, color = (1, 0, 0, 0.8), margin = 10): 694 if margin: 695 p1, p2 = self.ApplyLineMargin(p1, p2, margin, margin) 696 line = VectorLineTrace(parent=self, lineWidth=1, idx=0) 697 x1, y1 = p1 698 x2, y2 = p2 699 line.AddPoint((x1, y1), color) 700 line.AddPoint((x2, y2), color) 701 return line 702 703 704 class HexMapPathfinderInterface(StandardPathfinderInterface): 705 706 def __init__(self): 707 StandardPathfinderInterface.__init__(self) 708 self.blockedSpots = [] 709 self.usedSpots = [] 710 711 def GetAvoidanceList(self): 712 return self.blockedSpots + self.usedSpots 713 714 def GetSecurityPenalty(self): 715 return 0.0