materialGroups.py (13057B)
1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\eve\client\script\ui\shared\industry\views\materialGroups.py 2 import math 3 from math import acos, cos, sin, asin, pi, fabs 4 import carbonui.const as uiconst 5 from carbonui.primitives.container import Container 6 from carbonui.primitives.stretchspritehorizontal import StretchSpriteHorizontal 7 from carbonui.util.color import Color 8 from eve.client.script.ui.shared.industry.views.materialGroupDashedCircle import MaterialGroupDashedCircle 9 import industry 10 from localization import GetByLabel 11 from eve.client.script.ui.control.eveLabel import EveCaptionMedium 12 from eve.client.script.ui.shared.industry import industryUIConst 13 from eve.client.script.ui.shared.industry.views.industryLine import IndustryLineTrace 14 from eve.client.script.ui.shared.industry.views.materialGroup import MaterialGroup 15 import geo2 16 import uthread 17 from carbonui.uicore import uicore 18 BIGCIRCLE_OFFSET = 100 19 TOTAL_ROWS = 6 20 GROUP_NUMROWS = ((6,), 21 (3, 3), 22 (2, 2, 2), 23 (2, 1, 1, 2), 24 (1, 1, 2, 1, 1), 25 (1, 1, 1, 1, 1, 1)) 26 GROUP_BACKGROUNDS = {GROUP_NUMROWS[0]: 'res:/UI/Texture/Classes/Industry/Input/bg1Groups.png', 27 GROUP_NUMROWS[1]: 'res:/UI/Texture/Classes/Industry/Input/bg2Groups.png', 28 GROUP_NUMROWS[2]: 'res:/UI/Texture/Classes/Industry/Input/bg3Groups.png', 29 GROUP_NUMROWS[3]: 'res:/UI/Texture/Classes/Industry/Input/bg4Groups.png', 30 GROUP_NUMROWS[4]: 'res:/UI/Texture/Classes/Industry/Input/bg5Groups.png', 31 GROUP_NUMROWS[5]: 'res:/UI/Texture/Classes/Industry/Input/bg6Groups.png', 32 (2, 3, 1): 'res:/UI/Texture/Classes/Industry/Input/bgGroups_2_3_1.png', 33 (3, 1, 1, 1): 'res:/UI/Texture/Classes/Industry/Input/bgGroups_3_1_1_1.png', 34 (4, 2): 'res:/UI/Texture/Classes/Industry/Input/bgGroups_4_2.png', 35 (5, 1): 'res:/UI/Texture/Classes/Industry/Input/bgGroups_5_1.png'} 36 37 class MaterialGroups(Container): 38 default_name = 'MaterialGroups' 39 __notifyevents__ = ['OnMultipleItemChange', 'OnUIScalingChange'] 40 41 def ApplyAttributes(self, attributes): 42 Container.ApplyAttributes(self, attributes) 43 sm.RegisterNotify(self) 44 self.jobData = attributes.jobData 45 self.materialGroups = [] 46 self.lines = [] 47 self.fromLine = None 48 self.bigConnectorCircle = None 49 self.animateTextThread = None 50 noItemsLabelCont = Container(name='noItemsLabelCont', parent=self, align=uiconst.TOPLEFT_PROP, pos=(0.0, 0.5, 0.5, 500), padRight=industryUIConst.RADIUS_CENTERCIRCLE_OUTER) 51 self.noItemsRequiredLabel = EveCaptionMedium(name='noItemsRequiredLabel', parent=noItemsLabelCont, align=uiconst.CENTER, opacity=0.0, maxWidth=250) 52 self.bg = StretchSpriteHorizontal(name='groupBackground', parent=self, align=uiconst.TOLEFT_PROP, state=uiconst.UI_DISABLED, width=0.5, height=402, leftEdgeSize=10, rightEdgeSize=400) 53 groupsData = self.GetMaterialGroups() 54 self.ConstructGroups(groupsData) 55 self.ConstructLines(groupsData) 56 self.UpdateState() 57 self.AnimEntry() 58 59 def DrawBigConnectorCircle(self, groupsData): 60 x, y = self.GetPointBigCircleLeft() 61 if not self.bigConnectorCircle: 62 self.bigConnectorCircle = MaterialGroupDashedCircle(name='bigConnectorCircle', parent=self, radius=industryUIConst.RADIUS_CONNECTOR_LARGE, color=industryUIConst.COLOR_READY, numSegments=len(groupsData), lineWidth=3.0, materialsByGroupID=groupsData, jobData=self.jobData) 63 self.bigConnectorCircle.UpdateState(1) 64 self.bigConnectorCircle.left = x 65 self.bigConnectorCircle.top = y - industryUIConst.RADIUS_CONNECTOR_LARGE 66 67 def ConstructLines(self, groupsData): 68 for line in self.lines: 69 line.Close() 70 71 if self.fromLine: 72 self.fromLine.Close() 73 self.lines = [] 74 if self.materialGroups: 75 self.DrawBigConnectorCircle(groupsData) 76 self.DrawLines() 77 if self.jobData: 78 self.DrawLineFromCenter() 79 80 def GetStatusText(self): 81 if self.jobData: 82 status = self.jobData.status 83 if status == industry.STATUS_UNSUBMITTED: 84 text = GetByLabel('UI/Industry/NoInputItemsRequired') 85 elif status == industry.STATUS_INSTALLED: 86 text = GetByLabel('UI/Industry/JobActive') 87 elif status == industry.STATUS_READY: 88 text = GetByLabel('UI/Industry/JobReadyForDelivery') 89 else: 90 text = self.jobData.GetJobStateLabel() 91 else: 92 text = GetByLabel('UI/Industry/NoBlueprintSelectedCaption') 93 return text 94 95 def ConstructGroups(self, groupsData): 96 for group in self.materialGroups: 97 group.Close() 98 99 self.materialGroups = [] 100 numRowsByGroupIdx = self._GetNumRowsByGroup(groupsData) 101 bgTexturePath = GROUP_BACKGROUNDS.get(numRowsByGroupIdx, 'res:/UI/Texture/Classes/Industry/Input/bg1Groups.png') 102 if groupsData: 103 top = 1 104 for i, (industryGroupID, materialsData) in enumerate(groupsData): 105 numRows = numRowsByGroupIdx[i] 106 height = 60 * numRows + 8 * max(0, numRows - 1) 107 materialGroup = MaterialGroup(parent=self, industryGroupID=industryGroupID, jobData=self.jobData, materialsData=materialsData, pos=(0, 108 top, 109 400, 110 height), numRows=numRows, opacity=0.0) 111 top += height + 8 112 self.materialGroups.append(materialGroup) 113 114 statusText = '' 115 else: 116 statusText = self.GetStatusText() 117 self.AnimateStateText(statusText) 118 self.bg.SetTexturePath(bgTexturePath) 119 120 def _GetNumRowsByGroup(self, groupsData): 121 numGroups = len(groupsData) 122 numGroupsIdx = max(numGroups - 1, 0) 123 numRowsByGroup = GROUP_NUMROWS[numGroupsIdx] 124 MAX_MATERIAL_PER_ROW = 5 125 rowsNeeded = [] 126 for industryGroupID, materialsData in groupsData: 127 numMaterials = len(materialsData) 128 numRows = int(math.ceil(float(numMaterials) / MAX_MATERIAL_PER_ROW)) 129 rowsNeeded.append(numRows) 130 131 numRowsNeeded = sum(rowsNeeded) 132 if numRowsNeeded > TOTAL_ROWS: 133 return numRowsByGroup 134 for shouldBe, needed in zip(numRowsByGroup, rowsNeeded): 135 if needed > shouldBe: 136 if numRowsNeeded < TOTAL_ROWS: 137 lastRowValue = rowsNeeded[-1] 138 newLastRowValue = lastRowValue + (TOTAL_ROWS - numRowsNeeded) 139 rowsNeeded[-1] = newLastRowValue 140 return tuple(rowsNeeded) 141 142 return numRowsByGroup 143 144 def AnimateStateText(self, text): 145 if self.animateTextThread: 146 self.animateTextThread.kill() 147 self.animateTextThread = uthread.new(self._AnimateStateText, text) 148 149 def _AnimateStateText(self, text): 150 if self.noItemsRequiredLabel.text: 151 uicore.animations.FadeOut(self.noItemsRequiredLabel, duration=0.3, sleep=True) 152 self.noItemsRequiredLabel.text = text 153 uicore.animations.FadeTo(self.noItemsRequiredLabel, 0.0, 0.9, duration=0.6) 154 self.animateTextThread = None 155 156 def GetInnerCircleIntersectPoint(self, y): 157 pCenter = self.GetPointCenter() 158 y = pCenter[1] - y 159 th = asin(y / industryUIConst.RADIUS_CENTERCIRCLE_OUTER) + pi 160 r = industryUIConst.RADIUS_CENTERCIRCLE_INNER 161 p = (r * cos(th), r * sin(th)) 162 p = geo2.Vec2Add(p, pCenter) 163 return (p, th) 164 165 def GetOuterCirclePoint(self, p, p1): 166 l = industryUIConst.RADIUS_CENTERCIRCLE_OUTER - industryUIConst.RADIUS_CENTERCIRCLE_INNER 167 t = 1.0 - (l - industryUIConst.RADIUS_CONNECTOR_SMALL) / l 168 return geo2.Vec2Lerp(p, p1, t) 169 170 def DrawLine(self, points): 171 line = IndustryLineTrace(parent=self, opacity=0.0) 172 line.AddPoints(points) 173 self.lines.append(line) 174 175 def DrawLineFromCenter(self): 176 self.fromLine = IndustryLineTrace(name='fromLine', parent=self, opacity=0.0) 177 self.fromLine.AddPoints((self.GetPointCenter(), self.GetPointCenterRight())) 178 179 def DrawLineToCenter(self, p): 180 self.DrawLine((p, self.GetPointBigCircleLeft())) 181 self.DrawLine((self.GetPointBigCircleRight(), self.GetPointCenter())) 182 183 def GetPointCenter(self): 184 width, height = self.GetAbsoluteSize() 185 return (width / 2, height / 2 + 1) 186 187 def GetPointBigCircleLeft(self): 188 x, y = self.GetPointCenter() 189 return (x - BIGCIRCLE_OFFSET - industryUIConst.RADIUS_CONNECTOR_LARGE, y) 190 191 def GetPointBigCircleRight(self): 192 x, y = self.GetPointCenter() 193 return (x - BIGCIRCLE_OFFSET + industryUIConst.RADIUS_CONNECTOR_LARGE, y) 194 195 def GetPointCenterLeft(self): 196 x, y = self.GetPointCenter() 197 return (x - industryUIConst.RADIUS_CENTERCIRCLE_INNER, y) 198 199 def GetPointCenterRight(self): 200 w, h = self.GetCurrentAbsoluteSize() 201 return (w - 334, h / 2.0) 202 203 def DrawLines(self): 204 x1, y1 = self.GetAbsolutePosition() 205 linePoints = [] 206 thetas = [] 207 for materialGroup in self.materialGroups: 208 pCircleIntersect = materialGroup.GetEndPoint() 209 if pCircleIntersect is None: 210 continue 211 x0, y0 = pCircleIntersect 212 p = (x0, materialGroup.top + materialGroup.height / 2.0) 213 p1, theta = self.GetInnerCircleIntersectPoint(p[1]) 214 thetas.append(theta) 215 p0 = self.GetOuterCirclePoint(p, p1) 216 linePoints.append((p0, p1)) 217 218 if len(linePoints) == 1: 219 p = linePoints[0][0] 220 self.DrawLineToCenter(p) 221 return 222 if not thetas: 223 return 224 thetaFirst = thetas[0] 225 thetaLast = thetas[-1] 226 numPoints = int(fabs((thetaFirst - thetaLast) * 8)) 227 stepSize = (thetaLast - thetaFirst) / numPoints 228 lineStart = linePoints.pop(0) 229 arcPoints = [lineStart[0]] 230 r = industryUIConst.RADIUS_CENTERCIRCLE_INNER 231 for i in xrange(numPoints): 232 th = thetaFirst + float(i) * stepSize 233 p = (r * cos(th), r * sin(th)) 234 p = geo2.Vec2Add(self.GetPointCenter(), p) 235 arcPoints.append(p) 236 237 lineEnd = linePoints.pop() 238 arcPoints.extend([lineEnd[1], lineEnd[0]]) 239 self.DrawLine(arcPoints) 240 for p0, p1 in linePoints: 241 self.DrawLine((p0, p1)) 242 243 self.DrawLineToCenter(self.GetPointCenterLeft()) 244 245 def AnimEntry(self, animate = True): 246 k = 0.05 247 for i, materialGroup in enumerate(self.materialGroups): 248 if animate: 249 uicore.animations.FadeTo(materialGroup, 0.0, 1.0, duration=0.3, timeOffset=k * i) 250 else: 251 materialGroup.opacity = 1.0 252 materialGroup.AnimEntry(k * i, animate=animate) 253 254 i = 0 255 for i, line in enumerate(self.lines): 256 line.AnimEntry(0.2, i, animate=animate) 257 258 if self.fromLine: 259 self.fromLine.AnimEntry(0.8, i + 1, animate=animate) 260 if self.jobData and animate: 261 uicore.animations.FadeTo(self.bg, 0.0, 1.0, duration=0.6) 262 263 def GetMaterialGroups(self): 264 if not self.jobData: 265 return [] 266 else: 267 return self.jobData.GetMaterialsByGroups() 268 269 def OnMultipleItemChange(self, items, change): 270 self.OnRunsChanged() 271 272 def OnRunsChanged(self): 273 for materialGroup in self.materialGroups: 274 materialGroup.OnRunsChanged() 275 276 self.UpdateState() 277 278 def OnNewJobData(self, jobData): 279 if self.destroyed: 280 return 281 self.jobData = jobData 282 groupsData = self.GetMaterialGroups() 283 if self.bigConnectorCircle: 284 self.bigConnectorCircle.Close() 285 self.bigConnectorCircle = None 286 self.ConstructGroups(groupsData) 287 self.ConstructLines(groupsData) 288 self.UpdateState(animate=False) 289 self.AnimEntry() 290 291 def UpdateState(self, animate = True): 292 isReady = self.IsAllGroupsReady() 293 for i, line in enumerate(self.lines): 294 line.UpdateColor(isReady, animate=animate) 295 296 if self.fromLine: 297 self.fromLine.UpdateColor(isReady, animate=animate) 298 if self.bigConnectorCircle: 299 self.bigConnectorCircle.UpdateState(self.GetNumGroupsReady(), self.IsAllGroupsReady()) 300 301 def IsAllGroupsReady(self): 302 for group in self.materialGroups: 303 if not group.IsReady(): 304 return False 305 306 return True 307 308 def GetNumGroupsReady(self): 309 return sum([ materialGroup.IsReady() for materialGroup in self.materialGroups ]) 310 311 def OnUIScalingChange(self, *args): 312 self.OnNewJobData(self.jobData) 313 314 def _OnResize(self, *args): 315 if not self.jobData: 316 return 317 for group in self.materialGroups: 318 group.ConstructLines() 319 320 self.ConstructLines(self.GetMaterialGroups()) 321 self.UpdateState(animate=False) 322 self.AnimEntry(animate=False)