templatePreviewWindow.py (19360B)
1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\eve\client\script\ui\shared\planet\templatePreviewWindow.py 2 from __future__ import print_function 3 import math 4 import random 5 import blue 6 import mathext 7 import monolithconfig 8 import evetypes 9 import uthread2 10 from carbonui import uiconst, Align, TextColor 11 from carbonui.text.styles import TextHeader, TextBody, TextDetail 12 from carbonui.control.button import Button 13 from carbonui.control.checkbox import Checkbox 14 from carbonui.control.slider import Slider 15 from carbonui.control.window import Window 16 from carbonui.primitives.container import Container 17 from carbonui.primitives.containerAutoSize import ContainerAutoSize 18 from carbonui.primitives.sprite import Sprite 19 from clientsettings.setting import UserSettingNumeric 20 from carbonui.uicore import uicore 21 from carbonui.util.color import Color 22 from eve.client.script.ui.control.infoIcon import MoreInfoIcon 23 from player_messaging.client.ui_message import message_player 24 from eve.client.script.ui.control.eveLoadingWheel import LoadingWheel 25 from eve.client.script.ui.shared.planet.colonyTemplateLoading import LoadPiTemplate 26 from eve.client.script.ui.shared.planet.planetConst import * 27 from eve.client.script.ui.shared.planet.planetUIPins import TemplatePins 28 from eve.common.script.planet.colonyData import ColonyData 29 from eve.common.script.planet.surfacePoint import SurfacePoint 30 from eve.common.script.util.planetCommon import RADIUS_DRILLAREAMIN, RADIUS_DRILLAREAMAX 31 import inventorycommon.const as invConst 32 from eveplanet.client.templates.templateConst import ExtractionTypes, ProcessedTypes, RefinedTypes, SpecializedTypes, AdvancedTypes, TemplateDictKeys, TemplatePinDataDictKeys 33 from localization import GetByLabel 34 from urllib3.packages.ordered_dict import OrderedDict 35 EXTRACT_PRODUCTS = set(ExtractionTypes.itervalues()) 36 PRODUCTION_PRODUCTS = set(ProcessedTypes.itervalues()).union(set(RefinedTypes.itervalues())).union(set(SpecializedTypes.itervalues())).union(set(AdvancedTypes.itervalues())) 37 COMMENT_MAX_WIDTH = 60 38 DEFAULT_DRILL_RADIUS = (RADIUS_DRILLAREAMIN + RADIUS_DRILLAREAMAX) / 2.0 39 RING_NAME = 'RotationGizmoRings' 40 MIN_RING = 2 41 MAX_RING = 999999 42 CURSOR_PATH = 'res:/UI/Cursor/cursor32_x2.png' 43 BASE_MIN_RANGE = 0.012 44 PI_TEMPLATE_FUSCATION_CONFIG_ID = 'pi_template_fuscation' 45 46 class TemplatePreviewWindow(Window): 47 default_windowInstanceID = u'TemplatePreviewWindow' 48 default_fixedWidth = 400 49 default_fixedHeight = 300 50 default_caption = localization.GetByLabel('UI/PI/TemplatePreviewWindow') 51 default_windowID = u'TemplatePreviewWindow' 52 default_analyticID = 'pi_template_preview_window' 53 surfacePoints = [] 54 55 def ApplyAttributes(self, attributes): 56 super(type(self), self).ApplyAttributes(attributes) 57 self.contentCont = None 58 self.template = attributes.get('template', None) 59 self.planet = attributes.get('clientPlanet', None) 60 self.verticalReversed = False 61 self.compressed = None 62 self.buildIndicatorPins = [] 63 self.pinMover = PinMover() 64 uthread2.start_tasklet(self.DoInit) 65 66 def DoInit(self): 67 self.lw = LoadingWheel(name='loadingWheel', parent=self.content, align=uiconst.CENTER, state=uiconst.UI_NORMAL, pos=(0, 0, 160, 160), opacity=1.0, idx=0) 68 self.lw.Show() 69 self.CompressPins(self.template.loadedTemplate[TemplateDictKeys.PinData]) 70 message_player(localization.GetByLabel('UI/PI/PreprocessDone')) 71 self.lw.Hide() 72 self.ConstructLayout() 73 self.FocusToCmdCtr() 74 75 def ConstructLayout(self): 76 self.contentCont = cont = ContainerAutoSize(name='contentCont', parent=self.content, align=uiconst.TOTOP, padding=8, callback=self.OnContSizeChanged) 77 comment = self.template.loadedTemplate[TemplateDictKeys.Comments] 78 comment = comment if len(comment) < COMMENT_MAX_WIDTH else u'{0}...'.format(comment[:COMMENT_MAX_WIDTH]) 79 TextHeader(name='commentLabel', parent=cont, text=comment, align=Align.TOTOP, state=uiconst.UI_NORMAL, bold=True) 80 TextBody(parent=cont, align=uiconst.TOTOP, text=localization.GetByLabel('UI/PI/PreviewDescription'), top=8) 81 ctrlCont = Container(name='ctrlCont', parent=cont, align=Align.TOTOP, top=8) 82 btn = Button(name='RefocusButton', parent=ctrlCont, align=uiconst.TOPRIGHT, pos=(0, 0, 32, 32), hint=localization.GetByLabel('UI/PI/FocusToCmdCtr'), func=self.FocusToCmdCtr, texturePath='res:/UI/Texture/Planet/icons/focusIcon.png') 83 ctrlCont.height = btn.height 84 ReRenderOnReturn = lambda *args: self.InvokeRerenderLines() 85 self.doCompressCb = Checkbox(align=uiconst.TOTOP, name='CompressCheckBox', text=localization.GetByLabel('UI/PI/DoCompress'), checked=True, hint=localization.GetByLabel('UI/PI/DoCompressHint'), parent=ctrlCont, enabled=not monolithconfig.enabled(PI_TEMPLATE_FUSCATION_CONFIG_ID), callback=ReRenderOnReturn, padRight=btn.width) 86 TextDetail(parent=cont, text=GetByLabel('UI/PI/Horiz'), align=Align.TOTOP, color=TextColor.SECONDARY) 87 self.ConstructSliders(cont) 88 ReRenderOnReturn() 89 self.DrawGizmo() 90 bottomCont = ContainerAutoSize(parent=cont, align=Align.TOTOP, alignMode=Align.TOTOP, top=16) 91 buttonCont = ContainerAutoSize(parent=bottomCont, align=Align.TORIGHT) 92 btn = Button(name='ConfirmButton', parent=buttonCont, align=uiconst.TOPRIGHT, label=localization.GetByLabel('UI/PI/Confirm'), func=self.Confirm) 93 bottomCont.minHeight = btn.height 94 95 def ConstructSliders(self, cont): 96 ySetting = UserSettingNumeric('pi_template_y', default_value=0.0, min_value=-180.0, max_value=180.0) 97 ySetting.set(0) 98 ySetting.on_change.connect(self.OnYSettingChanged) 99 self.ySlider = Slider(parent=cont, align=uiconst.TOTOP, setting=ySetting, on_dragging=self.OnYDragging) 100 TextDetail(parent=cont, text=GetByLabel('UI/PI/AxisA'), align=Align.TOTOP, color=TextColor.SECONDARY) 101 xSetting = UserSettingNumeric('pi_template_x', default_value=0.0, min_value=-180.0, max_value=180.0) 102 xSetting.set(0) 103 xSetting.on_change.connect(self.OnXSettingChanged) 104 self.xSlider = Slider(parent=cont, align=uiconst.TOTOP, setting=xSetting, on_dragging=self.OnXDragging) 105 TextDetail(parent=cont, text=GetByLabel('UI/PI/AxisB'), align=Align.TOTOP, color=TextColor.SECONDARY) 106 zSetting = UserSettingNumeric('pi_template_z', default_value=0.0, min_value=-180.0, max_value=180.0) 107 zSetting.set(0) 108 zSetting.on_change.connect(self.OnZSettingChanged) 109 self.zSlider = Slider(parent=cont, align=uiconst.TOTOP, setting=zSetting, on_dragging=self.OnZDragging) 110 111 def OnContSizeChanged(self, *args, **kwds): 112 if self.contentCont: 113 height = self.contentCont.height + self.contentCont.padTop + self.contentCont.padBottom 114 _, height = self.GetWindowSizeForContentSize(height=height) 115 self.SetFixedHeight(height) 116 117 def OnXSettingChanged(self, value, *args): 118 self.pinMover.x = value 119 self.InvokeRerenderLines() 120 121 def OnXDragging(self, slider, *args): 122 self.pinMover.x = slider.GetValue() 123 self.InvokeRerenderLines() 124 125 def OnYSettingChanged(self, value, *args): 126 self.pinMover.y = value 127 self.InvokeRerenderLines() 128 129 def OnYDragging(self, slider, *args): 130 self.pinMover.y = slider.GetValue() 131 self.InvokeRerenderLines() 132 133 def OnZSettingChanged(self, value, *args): 134 self.pinMover.z = value 135 self.InvokeRerenderLines() 136 137 def OnZDragging(self, slider, *args): 138 self.pinMover.z = slider.GetValue() 139 self.InvokeRerenderLines() 140 141 def UpdateSliderValues(self): 142 self.xSlider.SetValue(self.pinMover.x) 143 self.ySlider.SetValue(self.pinMover.y) 144 self.zSlider.SetValue(self.pinMover.z) 145 146 def InvokeRerenderLines(self, *args): 147 pinMover = self.pinMover 148 degX = 0 149 degY = 0 150 degZ = 0 151 try: 152 degX = -float(pinMover.x) 153 degY = float(pinMover.y) 154 degZ = float(pinMover.z) 155 except: 156 pass 157 158 self.ReRenderPins(degX, degY, degZ) 159 self.ReRenderLines(degX, degY, degZ) 160 161 def FocusToCmdCtr(self, *args): 162 sm.GetService('planetUI').planetNav.cameraController.camera.OrbitToSurfacePoint(self.surfacePoints.values()[0][0], newZoom=sm.GetService('planetUI').planetNav.cameraController.camera.zoomLinear, initStartPos=False) 163 164 def ReRenderPins(self, degX, degY, degZ): 165 self.surfacePoints = OrderedDict() 166 origPinData = self.template.loadedTemplate[TemplateDictKeys.PinData] 167 pinData = origPinData 168 if self.doCompressCb.checked: 169 pinData = self.CompressPins(pinData) 170 for pinCount, pin in enumerate(pinData): 171 origPin = self.template.loadedTemplate[TemplateDictKeys.PinData][pinCount] 172 point = SurfacePoint(theta=pin[TemplatePinDataDictKeys.Longi], phi=pin[TemplatePinDataDictKeys.Lat], radius=1.05) 173 self.surfacePoints[str(origPin)] = (point.GetRotatedSurfacePoint(degX, degY, degZ), pin[TemplatePinDataDictKeys.PinTypeID]) 174 175 if not self.buildIndicatorPins: 176 self.InitPins() 177 i = 0 178 for point, typeID in self.surfacePoints.values(): 179 newPoint = SurfacePoint(theta=point.theta, phi=point.phi, radius=1.0) 180 self.buildIndicatorPins[i].SetLocation(newPoint) 181 i += 1 182 183 def InitPins(self): 184 i = 0 185 for pin in self.buildIndicatorPins: 186 pin.Remove() 187 188 clientPlanet = sm.GetService('planetUI').GetCurrentPlanet() 189 colony = clientPlanet.GetColony(session.charid) 190 if colony is None: 191 colony = clientPlanet.GetNewColony(session.charid) 192 colony.SetColonyData(ColonyData(session.charid, None)) 193 for point, typeID in self.surfacePoints.values(): 194 newPoint = SurfacePoint(theta=point.theta, phi=point.phi, radius=1.0) 195 color = self._GetPinTypeColor(typeID) 196 newPin = TemplatePins(newPoint, typeID, evetypes.GetGroupID(typeID), sm.GetService('planetUI').pinOthersTransform) 197 newPin.SetCircleColor(color) 198 i += 1 199 newPin.SetLocation(newPoint) 200 self.buildIndicatorPins.append(newPin) 201 202 def ReRenderLines(self, degX, degY, degZ): 203 self.surfacePoints = OrderedDict() 204 origPinData = self.template.loadedTemplate[TemplateDictKeys.PinData] 205 pinData = origPinData 206 if self.doCompressCb.checked: 207 pinData = self.CompressPins(pinData) 208 for pinCount, pin in enumerate(pinData): 209 origPin = self.template.loadedTemplate[TemplateDictKeys.PinData][pinCount] 210 point = SurfacePoint(theta=pin[TemplatePinDataDictKeys.Longi], phi=pin[TemplatePinDataDictKeys.Lat], radius=1.05) 211 pinTypeID = pin[TemplatePinDataDictKeys.PinTypeID] 212 color = self._GetPinTypeColor(pinTypeID) 213 self.surfacePoints[str(origPin)] = (point.GetRotatedSurfacePoint(degX, degY, degZ), color) 214 215 def _GetPinTypeColor(self, pinTypeID): 216 if evetypes.GetGroupID(pinTypeID) in (invConst.groupStoragePins, invConst.groupSpaceportPins): 217 color = Color.BLUE 218 elif evetypes.GetGroupID(pinTypeID) == invConst.groupExtractionControlUnitPins: 219 color = Color.GREEN 220 elif pinTypeID in TYPEIDS_PROCESSORS: 221 color = Color.ORANGE 222 else: 223 color = Color.RED 224 return color 225 226 def Confirm(self, *args): 227 result = LoadPiTemplate(self.surfacePoints, self.template) 228 if result: 229 message_player(GetByLabel('UI/PI/LoadedFromTemplate')) 230 self.Close() 231 232 def CompressPins(self, pinData): 233 if self.compressed: 234 return self.compressed 235 forProcessing = [] 236 for id, pin in enumerate(pinData): 237 forProcessing.append([id, [pin[TemplatePinDataDictKeys.Longi], pin[TemplatePinDataDictKeys.Lat]]]) 238 239 minDist = BASE_MIN_RANGE 240 doFuscation = 1 if monolithconfig.enabled(PI_TEMPLATE_FUSCATION_CONFIG_ID) else 0 241 forProcessing = adjust_coordinates(forProcessing, minDist, 2, doFuscation) 242 forProcessing = sorted(forProcessing, key=lambda coord: coord[0]) 243 self.compressed = [] 244 for id, pin in enumerate(forProcessing): 245 self.compressed.append({TemplatePinDataDictKeys.PinTypeID: pinData[id][TemplatePinDataDictKeys.PinTypeID], 246 TemplatePinDataDictKeys.Longi: pin[1][0], 247 TemplatePinDataDictKeys.Lat: pin[1][1]}) 248 249 return self.compressed 250 251 def DrawGizmo(self, len = 1.1): 252 CLD = sm.GetService('planetUI').curveLineDrawer 253 try: 254 CLD.ClearLines(RING_NAME) 255 except: 256 pass 257 258 CLD.CreateLineSet(RING_NAME, sm.GetService('planetUI').planetTransform) 259 RED = (1, 0.2, 0.2, 5) 260 BLUE = (0.2, 1, 0.2, 5) 261 GREEN = (0.2, 0.2, 1, 5) 262 for i in range(91): 263 i = i * 4 + 0.5 264 CLD.DrawLine(RING_NAME, SurfacePoint(math.sin(math.radians(i - 1)) * len, 0, math.cos(math.radians(i - 1)) * len), SurfacePoint(math.sin(math.radians(i)) * len, 0, math.cos(math.radians(i)) * len), 3.0, BLUE, BLUE) 265 266 for i in range(91): 267 i = i * 4 + 0.5 268 CLD.DrawLine(RING_NAME, SurfacePoint(0, math.sin(math.radians(i - 1)) * len, math.cos(math.radians(i - 1)) * len), SurfacePoint(0, math.sin(math.radians(i)) * len, math.cos(math.radians(i)) * len), 3.0, RED, RED) 269 270 for i in range(91): 271 i = i * 4 + 0.5 272 CLD.DrawLine(RING_NAME, SurfacePoint(math.sin(math.radians(i - 1)) * len, math.cos(math.radians(i - 1)) * len, 0), SurfacePoint(math.sin(math.radians(i)) * len, math.cos(math.radians(i)) * len, 0), 3.0, GREEN, GREEN) 273 274 CLD.SubmitLineset(RING_NAME) 275 276 def Close(self, **kwargs): 277 super(TemplatePreviewWindow, self).Close(kwargs) 278 self._Close() 279 280 def _Close(self, *args): 281 try: 282 for pin in self.buildIndicatorPins: 283 pin.Remove() 284 285 cld = sm.GetService('planetUI').curveLineDrawer 286 cld.ClearLines(RING_NAME) 287 cld.SubmitLineset(RING_NAME) 288 lineSet = cld.GetLineSet(RING_NAME) 289 if lineSet in sm.GetService('planetUI').planetTransform.children: 290 sm.GetService('planetUI').planetTransform.children.remove(lineSet) 291 cld.RemoveLineSet(RING_NAME) 292 except Exception as e: 293 print('except = ', e) 294 295 296 class PinMover(object): 297 298 def __init__(self): 299 self._x = 0 300 self._y = 0 301 self._z = 0 302 self._theta = 0 303 304 @property 305 def x(self): 306 return self._x 307 308 @x.setter 309 def x(self, value): 310 if value > 180: 311 value -= 360 312 elif value < -180: 313 value += 360 314 self._x = self._ClampValue(value) 315 316 @property 317 def y(self): 318 return self._y 319 320 @y.setter 321 def y(self, value): 322 self._y = self._ClampValue(value) 323 324 @property 325 def z(self): 326 return self._z 327 328 @z.setter 329 def z(self, value): 330 self._z = self._ClampValue(value) 331 332 def _ClampValue(self, value): 333 if value > 180: 334 value -= 360 335 elif value < -180: 336 value += 360 337 return mathext.clamp(value, -180, 180) 338 339 340 def StartDraggingTemplate(x, y): 341 wnd = TemplatePreviewWindow.GetIfOpen() 342 if not wnd: 343 return 344 firstPinTheta = wnd.template.loadedTemplate[TemplateDictKeys.PinData][0][TemplatePinDataDictKeys.Longi] 345 originalX = wnd.pinMover.x 346 originalY = wnd.pinMover.y 347 originalZ = wnd.pinMover.z 348 uthread2.StartTasklet(UpdateOften, wnd, x, y, originalX, originalY, originalZ, firstPinTheta) 349 350 351 def UpdateOften(wnd, prevX, prevY, originalX, originalY, originalZ, theta = 0): 352 isMouseDown = uicore.uilib.leftbtn 353 if wnd.destroyed or not isMouseDown: 354 return 355 zoomLinear = sm.GetService('planetUI').planetNav.cameraController.camera.zoomLinear 356 x = uicore.uilib.x 357 y = uicore.uilib.y 358 pinMover = wnd.pinMover 359 newY = (x - prevX) * zoomLinear * 0.2 + originalY 360 pinMover.y = newY 361 newX = (y - prevY) * -math.sin(theta) * zoomLinear * 0.2 + originalX 362 pinMover.x = newX 363 newZ = (y - prevY) * -math.cos(theta) * zoomLinear * 0.2 + originalZ 364 pinMover.z = newZ 365 compareVal = 75 * zoomLinear * 0.75 366 if math.fabs(originalX - pinMover.x) > compareVal or math.fabs(originalY - pinMover.y) > compareVal: 367 wnd.FocusToCmdCtr() 368 wnd.UpdateSliderValues() 369 wnd.InvokeRerenderLines() 370 uthread2.Yield() 371 uthread2.start_tasklet(UpdateOften, wnd, prevX, prevY, originalX, originalY, originalZ, theta) 372 373 374 def scale_away(coord, target, factor): 375 x_diff = coord[1][0] - target[1][0] 376 y_diff = coord[1][1] - target[1][1] 377 return [coord[0], [target[1][0] + factor * x_diff, target[1][1] + factor * y_diff]] 378 379 380 def move_closer(coord, target, step_size): 381 x_diff = target[1][0] - coord[1][0] 382 y_diff = target[1][1] - coord[1][1] 383 distance = (x_diff ** 2 + y_diff ** 2) ** 0.5 384 if distance == 0: 385 return coord 386 return [coord[0], [coord[1][0] + step_size * x_diff / distance, coord[1][1] + step_size * y_diff / distance]] 387 388 389 def distance_between(coord1, coord2): 390 SP1 = SurfacePoint(theta=coord1[1][0], phi=coord1[1][1]) 391 SP2 = SurfacePoint(theta=coord2[1][0], phi=coord2[1][1]) 392 return SP1.GetDistanceToOther(SP2) 393 394 395 def is_move_valid(coord, other_coords, MinDist): 396 for other in other_coords: 397 if distance_between(coord, other) < MinDist: 398 return False 399 400 return True 401 402 403 def adjust_coordinates(coordList, minDist, expansion_factor, forcedFuscation = 0): 404 target = coordList[0] 405 expanded_coords = sorted(coordList[1:], key=lambda coord: distance_between(coord, target)) 406 expanded_coords = [ scale_away(coord, target, expansion_factor + forcedFuscation * expansion_factor * random.random()) for coord in expanded_coords ] 407 adjusted_coords = [target] + expanded_coords 408 step_size = minDist * 0.01 409 max_iterations = 10000 410 iteration = 0 411 unchangedIterationCount = 0 412 message_player(localization.GetByLabel('UI/PI/PreprocessingMsg', pct=0)) 413 while iteration < max_iterations: 414 somethingChanged = False 415 for i in range(1, len(adjusted_coords)): 416 new_position = move_closer(adjusted_coords[i], target, step_size) 417 if is_move_valid(new_position, adjusted_coords[:i] + adjusted_coords[i + 1:], minDist): 418 if adjusted_coords[i] != new_position: 419 somethingChanged = True 420 adjusted_coords[i] = new_position 421 422 iteration += 1 423 if iteration % 30 == 0: 424 if not TemplatePreviewWindow.GetIfOpen(): 425 message_player(localization.GetByLabel('UI/PI/PreprocessTerminated')) 426 return 427 if iteration % 1000 == 0: 428 message_player(localization.GetByLabel('UI/PI/PreprocessingMsg', pct=iteration * 100.0 / max_iterations), flash_existing=False) 429 if somethingChanged: 430 unchangedIterationCount = 0 431 else: 432 if unchangedIterationCount > 20: 433 break 434 unchangedIterationCount += 1 435 blue.pyos.BeNice() 436 437 return adjusted_coords