def ConstructLines(self): for line in self.lines: line.Close() self.lines = [] usedRows = int(ceil(len(self.materialsData) / float(self.perRow))) pointsStart = self.GetLineStartingPoints() pConnect = self.GetLineConnectionPoint(pointsStart) y = self.top + self.materialsCont.top + self.materialsCont.height / 2 x = self.GetLineCirceOffset(y) width, height = self.parent.GetCurrentAbsoluteSize() self.pCircleIntersect = (width / 2 - x, pConnect[1]) pEnd = geo2.Vec2Subtract(self.pCircleIntersect, (1 * RADIUS_CONNECTOR_SMALL, 0)) if len(pointsStart) == 1: self.DrawLine((pointsStart[0], pEnd)) else: for point in pointsStart: if point[1] == pEnd[1]: self.DrawLine((point, geo2.Vec2Subtract(pConnect, (3, 0)))) else: self.DrawLine((point, (pConnect[0], point[1]), pConnect)) self.DrawLine((geo2.Vec2Add(pConnect, (3, 0)), pEnd)) if not self.connectorCircle: self.connectorCircle = MaterialGroupDashedCircle( parent=self.materialsCont, numSegments=len(self.materialsData), radius=RADIUS_CONNECTOR_SMALL, materialsByGroupID=((self.industryGroupID, self.materialsData), ), jobData=self.jobData) self.connectorCircle.left = pEnd[0] self.connectorCircle.top = pEnd[1] - RADIUS_CONNECTOR_SMALL self.UpdateState(animate=False)
def DrawBigConnectorCircle(self, groupsData): x, y = self.GetPointBigCircleLeft() if not self.bigConnectorCircle: 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) self.bigConnectorCircle.UpdateState(1) self.bigConnectorCircle.left = x self.bigConnectorCircle.top = y - industryUIConst.RADIUS_CONNECTOR_LARGE
class MaterialGroups(Container): default_name = 'MaterialGroups' __notifyevents__ = ['OnMultipleItemChange', 'OnUIScalingChange'] def ApplyAttributes(self, attributes): Container.ApplyAttributes(self, attributes) sm.RegisterNotify(self) self.jobData = attributes.jobData self.materialGroups = [] self.lines = [] self.fromLine = None self.bigConnectorCircle = None self.animateTextThread = None noItemsLabelCont = Container( parent=self, align=uiconst.TOPLEFT_PROP, pos=(0.0, 0.5, 0.5, 500), padRight=industryUIConst.RADIUS_CENTERCIRCLE_OUTER) self.noItemsRequiredLabel = EveCaptionMedium( name='noItemsRequiredLabel', parent=noItemsLabelCont, align=uiconst.CENTER, opacity=0.0) self.bg = StretchSpriteHorizontal(name='groupBackground', parent=self, align=uiconst.TOLEFT_PROP, state=uiconst.UI_DISABLED, width=0.5, height=402, leftEdgeSize=10, rightEdgeSize=400) groupsData = self.GetMaterialGroups() self.ConstructGroups(groupsData) self.ConstructLines(groupsData) self.UpdateState() self.AnimEntry() def DrawBigConnectorCircle(self, groupsData): x, y = self.GetPointBigCircleLeft() if not self.bigConnectorCircle: 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) self.bigConnectorCircle.UpdateState(1) self.bigConnectorCircle.left = x self.bigConnectorCircle.top = y - industryUIConst.RADIUS_CONNECTOR_LARGE def ConstructLines(self, groupsData): for line in self.lines: line.Close() if self.fromLine: self.fromLine.Close() self.lines = [] if self.materialGroups: self.DrawBigConnectorCircle(groupsData) self.DrawLines() if self.jobData: self.DrawLineFromCenter() def GetStatusText(self): if self.jobData: status = self.jobData.status if status == industry.STATUS_UNSUBMITTED: text = GetByLabel('UI/Industry/NoInputItemsRequired') elif status == industry.STATUS_INSTALLED: text = GetByLabel('UI/Industry/JobActive') elif status == industry.STATUS_READY: text = GetByLabel('UI/Industry/JobReadyForDelivery') else: text = self.jobData.GetJobStateLabel() else: text = GetByLabel('UI/Industry/NoBlueprintSelectedCaption') return text def ConstructGroups(self, groupsData): for group in self.materialGroups: group.Close() self.materialGroups = [] numGroups = max(len(groupsData) - 1, 0) numRowsByGroupIdx = GROUP_NUMROWS[numGroups] if groupsData: top = 1 for i, (industryGroupID, materialsData) in enumerate(groupsData): numRows = numRowsByGroupIdx[i] height = 60 * numRows + 8 * max(0, numRows - 1) materialGroup = MaterialGroup(parent=self, industryGroupID=industryGroupID, jobData=self.jobData, materialsData=materialsData, pos=(0, top, 400, height), numRows=numRows, opacity=0.0) top += height + 8 self.materialGroups.append(materialGroup) statusText = '' else: statusText = self.GetStatusText() self.AnimateStateText(statusText) self.bg.SetTexturePath(GROUP_BACKGROUNDS[numGroups]) def AnimateStateText(self, text): if self.animateTextThread: self.animateTextThread.kill() self.animateTextThread = uthread.new(self._AnimateStateText, text) def _AnimateStateText(self, text): if self.noItemsRequiredLabel.text: uicore.animations.FadeOut(self.noItemsRequiredLabel, duration=0.3, sleep=True) self.noItemsRequiredLabel.text = text uicore.animations.FadeTo(self.noItemsRequiredLabel, 0.0, 0.9, duration=0.6) self.animateTextThread = None def GetInnerCircleIntersectPoint(self, y): """ Returns inner circle intersection point and angle (in radians) of line coming from a group, given y-coordinate of that line """ pCenter = self.GetPointCenter() y = pCenter[1] - y th = asin(y / industryUIConst.RADIUS_CENTERCIRCLE_OUTER) + pi r = industryUIConst.RADIUS_CENTERCIRCLE_INNER p = (r * cos(th), r * sin(th)) p = geo2.Vec2Add(p, pCenter) return (p, th) def GetOuterCirclePoint(self, p, p1): """ Offset point by radius of connector circle """ l = industryUIConst.RADIUS_CENTERCIRCLE_OUTER - industryUIConst.RADIUS_CENTERCIRCLE_INNER t = 1.0 - (l - industryUIConst.RADIUS_CONNECTOR_SMALL) / l return geo2.Vec2Lerp(p, p1, t) def DrawLine(self, points): line = IndustryLineTrace(parent=self, opacity=0.0) line.AddPoints(points) self.lines.append(line) def DrawLineFromCenter(self): self.fromLine = IndustryLineTrace(name='fromLine', parent=self, opacity=0.0) self.fromLine.AddPoints( (self.GetPointCenter(), self.GetPointCenterRight())) def DrawLineToCenter(self, p): self.DrawLine((p, self.GetPointBigCircleLeft())) self.DrawLine((self.GetPointBigCircleRight(), self.GetPointCenter())) def GetPointCenter(self): width, height = self.GetAbsoluteSize() return (width / 2, height / 2 + 1) def GetPointBigCircleLeft(self): x, y = self.GetPointCenter() return (x - BIGCIRCLE_OFFSET - industryUIConst.RADIUS_CONNECTOR_LARGE, y) def GetPointBigCircleRight(self): x, y = self.GetPointCenter() return (x - BIGCIRCLE_OFFSET + industryUIConst.RADIUS_CONNECTOR_LARGE, y) def GetPointCenterLeft(self): x, y = self.GetPointCenter() return (x - industryUIConst.RADIUS_CENTERCIRCLE_INNER, y) def GetPointCenterRight(self): w, h = self.GetCurrentAbsoluteSize() return (w - 334, h / 2.0) def DrawLines(self): """ Draw lines connecting materialGroups to center circle """ x1, y1 = self.GetAbsolutePosition() linePoints = [] thetas = [] for materialGroup in self.materialGroups: x0, y0 = materialGroup.GetEndPoint() p = (x0, materialGroup.top + materialGroup.height / 2.0) p1, theta = self.GetInnerCircleIntersectPoint(p[1]) thetas.append(theta) p0 = self.GetOuterCirclePoint(p, p1) linePoints.append((p0, p1)) if len(linePoints) == 1: p = linePoints[0][0] self.DrawLineToCenter(p) return thetaFirst = thetas[0] thetaLast = thetas[-1] numPoints = int(fabs((thetaFirst - thetaLast) * 8)) stepSize = (thetaLast - thetaFirst) / numPoints lineStart = linePoints.pop(0) arcPoints = [lineStart[0]] r = industryUIConst.RADIUS_CENTERCIRCLE_INNER for i in xrange(numPoints): th = thetaFirst + float(i) * stepSize p = (r * cos(th), r * sin(th)) p = geo2.Vec2Add(self.GetPointCenter(), p) arcPoints.append(p) lineEnd = linePoints.pop() arcPoints.extend([lineEnd[1], lineEnd[0]]) self.DrawLine(arcPoints) for p0, p1 in linePoints: self.DrawLine((p0, p1)) self.DrawLineToCenter(self.GetPointCenterLeft()) def AnimEntry(self, animate=True): k = 0.05 for i, materialGroup in enumerate(self.materialGroups): if animate: uicore.animations.FadeTo(materialGroup, 0.0, 1.0, duration=0.3, timeOffset=k * i) else: materialGroup.opacity = 1.0 materialGroup.AnimEntry(k * i, animate=animate) i = 0 for i, line in enumerate(self.lines): line.AnimEntry(0.2, i, animate=animate) if self.fromLine: self.fromLine.AnimEntry(0.8, i + 1, animate=animate) if self.jobData and animate: uicore.animations.FadeTo(self.bg, 0.0, 1.0, duration=0.6) def GetMaterialGroups(self): if not self.jobData: return [] else: return self.jobData.GetMaterialsByGroups() def OnMultipleItemChange(self, items, change): self.OnRunsChanged() def OnRunsChanged(self): for materialGroup in self.materialGroups: materialGroup.OnRunsChanged() self.UpdateState() def OnNewJobData(self, jobData): self.jobData = jobData groupsData = self.GetMaterialGroups() if self.bigConnectorCircle: self.bigConnectorCircle.Close() self.bigConnectorCircle = None self.ConstructGroups(groupsData) self.ConstructLines(groupsData) self.UpdateState(animate=False) self.AnimEntry() def UpdateState(self, animate=True): isReady = self.IsAllGroupsReady() for i, line in enumerate(self.lines): line.UpdateColor(isReady, animate=animate) if self.fromLine: self.fromLine.UpdateColor(isReady, animate=animate) if self.bigConnectorCircle: self.bigConnectorCircle.UpdateState(self.GetNumGroupsReady(), self.IsAllGroupsReady()) def IsAllGroupsReady(self): for group in self.materialGroups: if not group.IsReady(): return False return True def GetNumGroupsReady(self): return sum( [materialGroup.IsReady() for materialGroup in self.materialGroups]) def OnUIScalingChange(self, *args): self.OnNewJobData(self.jobData) def _OnResize(self, *args): if not self.jobData: return for group in self.materialGroups: group.ConstructLines() self.ConstructLines(self.GetMaterialGroups()) self.UpdateState(animate=False) self.AnimEntry(animate=False)
class MaterialGroup(Container): default_name = 'MaterialGroup' default_height = 60 default_alignMode = uiconst.TOPLEFT default_align = uiconst.TOPLEFT def ApplyAttributes(self, attributes): Container.ApplyAttributes(self, attributes) self.jobData = attributes.jobData self.materialsData = attributes.materialsData self.numRows = attributes.numRows self.industryGroupID = attributes.industryGroupID self.perRow = self.GetNumPerRow() self.materials = [] self.lines = [] self.connectorCircle = None self.pCircleIntersect = None self.materialsCont = ContainerAutoSize(name='materialsCont', parent=self, align=uiconst.TOPLEFT, alignMode=uiconst.TOPLEFT) self.icon = GroupIconSprite(name='icon', parent=self, align=uiconst.CENTERLEFT, pos=(7, 0, 22, 22), industryGroupID=self.industryGroupID) self.ConstructMaterials() self.ConstructLines() def AnimEntry(self, timeOffset, animate=True): for i, material in enumerate(self.materials): if animate: uicore.animations.FadeTo(material, 0.0, 1.0, duration=0.3, timeOffset=timeOffset + i * 0.02) else: material.opacity = 1.0 for i, line in enumerate(self.lines): line.AnimEntry(timeOffset, i, animate) if animate: uicore.animations.FadeTo(self.connectorCircle, 0.0, 1.0, duration=0.3, timeOffset=timeOffset) else: self.connectorCircle.opacity = 1.0 def ConstructLines(self): for line in self.lines: line.Close() self.lines = [] usedRows = int(ceil(len(self.materialsData) / float(self.perRow))) pointsStart = self.GetLineStartingPoints() pConnect = self.GetLineConnectionPoint(pointsStart) y = self.top + self.materialsCont.top + self.materialsCont.height / 2 x = self.GetLineCirceOffset(y) width, height = self.parent.GetCurrentAbsoluteSize() self.pCircleIntersect = (width / 2 - x, pConnect[1]) pEnd = geo2.Vec2Subtract(self.pCircleIntersect, (1 * RADIUS_CONNECTOR_SMALL, 0)) if len(pointsStart) == 1: self.DrawLine((pointsStart[0], pEnd)) else: for point in pointsStart: if point[1] == pEnd[1]: self.DrawLine((point, geo2.Vec2Subtract(pConnect, (3, 0)))) else: self.DrawLine((point, (pConnect[0], point[1]), pConnect)) self.DrawLine((geo2.Vec2Add(pConnect, (3, 0)), pEnd)) if not self.connectorCircle: self.connectorCircle = MaterialGroupDashedCircle( parent=self.materialsCont, numSegments=len(self.materialsData), radius=RADIUS_CONNECTOR_SMALL, materialsByGroupID=((self.industryGroupID, self.materialsData), ), jobData=self.jobData) self.connectorCircle.left = pEnd[0] self.connectorCircle.top = pEnd[1] - RADIUS_CONNECTOR_SMALL self.UpdateState(animate=False) def DrawLine(self, points): line = IndustryLineTrace(parent=self.materialsCont) line.AddPoints(points) self.lines.append(line) def GetEndPoint(self): return self.pCircleIntersect def ConstructMaterials(self): for i, materialData in enumerate(self.materialsData): top = 66 * (i / self.perRow) cls = self.GetMaterialClass() material = cls(parent=self.materialsCont, jobData=self.jobData, materialData=materialData, align=uiconst.TOPLEFT, left=ICONCOLUMN_WIDTH + i % self.perRow * 48, top=top) self.materials.append(material) self.materialsCont.SetSizeAutomatically() self.materialsCont.top = (self.height - self.materialsCont.height) / 2 def GetMaterialClass(self): if self.IsOptional(): return OptionalMaterial else: return Material def IsOptional(self): for materialData in self.materialsData: if not materialData.IsOptional(): return False return True def IsOptionSelected(self): """ Returns True unless all items are optional and none of them have an item selected """ for materialData in self.materialsData: if materialData.IsOptionSelected(): return True return False def GetLineStartingPoints(self): ret = defaultdict(int) for material in self.materials: x = material.left + Material.default_width y = material.top + Material.default_height / 2 ret[y] = max(ret[y], x) ret = zip(ret.values(), ret.keys()) return ret def OnRunsChanged(self): self.UpdateState() def UpdateState(self, animate=True): for line in self.lines: line.UpdateColor(self.IsReady(), self.IsOptional(), self.IsOptionSelected(), animate) numReadySegments = sum( [materialData.valid for materialData in self.materialsData]) self.connectorCircle.UpdateState(numReadySegments, self.IsReady(), self.IsOptionSelected(), animate=animate) def GetNumPerRow(self): numMaterials = len(self.materialsData) if self.numRows >= 2: if numMaterials > 3: return min(5, int(ceil(numMaterials / 2.0))) return numMaterials def GetLineConnectionPoint(self, points): """ Returs coordinates of point where all lines coming from the groups connect """ x = max((x for x, _ in points)) x += OFFSET_CONNECTIONPOINT y = sum((y for _, y in points)) / len(points) return (x, y) def GetLineCirceOffset(self, y): """ Returns x-part of intersection line with outer circle """ r = RADIUS_CENTERCIRCLE_OUTER y = float(y) return r * cos(asin((r - y) / r)) def IsReady(self): """ Are all materials in group owned in full quantity """ for materialData in self.materialsData: if not materialData.valid: return False return True