app/resources/qml/Components/SpeedFlowChart.qml

315 lines
12 KiB
QML
Raw Normal View History

import QtQuick 2.10
import QtQuick.Controls 2.4
import QtQuick.Controls.Material 2.1
ListView {
id: control
property var flowchartData
property var allFlowchartData
property int rounds
property int tileSize: app.height / 8 * 0.8
anchors.fill: parent
anchors.margins: 10
model: rounds + 2
spacing: app.width * 0.1
orientation: ListView.LeftToRight
boundsBehavior: ListView.StopAtBounds
onFlowchartDataChanged: {
prepareData()
}
function prepareData() {
// array to store the restructured data
var allData = []
for(var round in flowchartData['route_names']){
if(flowchartData['route_names'].hasOwnProperty(round) && parseInt(round) >= 0){
console.log(round)
if(allData.length > 0){
allData[allData.length-1].push(round)
allData[allData.length-1].push(flowchartData['route_names'][round])
}
if(parseInt(round) === 0){
// this is the first round
// find pairs (always wors vs. best (1-16; 1-15; ...)) (they are sorted by the rank of the better athlete (1-2-3-4-5-6-7-8)
var qualificationResults = []
for(var x = 0; x < flowchartData['participants'].length; x++){
qualificationResults.push(flowchartData['participants'][x])
}
qualificationResults.sort(function(a, b) {
return parseInt(a["result_rank0"]) - parseInt(b["result_rank0"]);
});
var nextRoundPairs = []
var nextRoundMatches = Math.pow(2, control.model-1)
for(var i = 0; i < nextRoundMatches; i++){
nextRoundPairs.push([qualificationResults[i], qualificationResults[nextRoundMatches*2-i-1]])
}
// build second round pairs (sorted by the rank of the better athlete and worst vs. best (1-8; 2-7; ... ))
var sortedFirstRoundPairs = []
for(i = 0; i < nextRoundMatches; i += 1){
sortedFirstRoundPairs.push([nextRoundPairs.shift(), nextRoundPairs.pop()])
}
// sort these pairs (containing two pairs of athletes) by the rank of the better athlete (1-4;2-3)
var finalSortedFirstRoundPairs = []
for(i=0; i < nextRoundMatches/4; i++){
finalSortedFirstRoundPairs.push(sortedFirstRoundPairs[i])
finalSortedFirstRoundPairs.push(sortedFirstRoundPairs[nextRoundMatches/2-i-1])
}
// convert the list of pairs of pairs of athletes back to a single list of pairs of athletes
var finalFirstRoundPairs = []
for(i=0; i < finalSortedFirstRoundPairs.length; i++){
finalFirstRoundPairs.push(finalSortedFirstRoundPairs[i][0])
finalFirstRoundPairs.push(finalSortedFirstRoundPairs[i][1])
}
// push the first round to all data
allData.push(finalFirstRoundPairs)
}
else if(parseInt(round) > 0 ){
// this is not the first round
var nextRound = []
// only used when the current round is the 1/2 final
var smallFinal = []
var Final = []
for(var i = 0; i < allData[allData.length-1].length-2; i+=1){
var thisPair = allData[allData.length-1][i]
var thisWinner
var thisLooser
var thisWinnerIsFirstOfNewPair = i%2 === 0
if(thisWinnerIsFirstOfNewPair){
nextRound.push([])
}
//thisPair[0]["result_rank"] = thisPair[0]["result_rank"+round]
//thisPair[1]["result_rank"] = thisPair[1]["result_rank"+round]
if(parseInt(thisPair[0]["result_rank"+round]) < parseInt(thisPair[1]["result_rank"+round])){
thisWinner = thisPair[0]
thisLooser = thisPair[1]
}
else {
thisWinner = thisPair[1]
thisLooser = thisPair[0]
}
console.log(thisWinner['firstname']+" has won in round " + round)
//thisWinner.win = false
//thisLooser.win = true
if(round - control.rounds === 2){
// if we are in the 1/2 final
Final.push(thisWinner)
smallFinal.push(thisLooser)
}
else {
nextRound[nextRound.length-1].push(thisWinner)
}
}
if(smallFinal.length > 0 && Final.length > 0){
// Final
allData.push([Final, parseInt(round)+2, flowchartData['route_names'][String(parseInt(round)+2)] + " / " + flowchartData['route_names'][String(parseInt(round)+1)] ])
// small Final
allData.push([smallFinal, parseInt(round)+1])
break
}
else {
//nextRound.push(round)
//nextRound.push(flowchartData['route_names'][round])
allData.push(nextRound)
}
}
}
}
control.allFlowchartData = allData
}
delegate: Column {
id: roundCol
property int thisIndex: index
property int thisRound: control.allFlowchartData[roundCol.thisIndex][control.allFlowchartData[roundCol.thisIndex].length-2]
property bool thisIsLastRound: thisIndex === control.model - 1
width: app.width * 0.5
height: control.height
//spacing: app.width * 0.1
Label {
id: roundNameLa
width: parent.width
horizontalAlignment: Text.AlignHCenter
font.bold: true
text: control.allFlowchartData[roundCol.thisIndex][control.allFlowchartData[roundCol.thisIndex].length-1]
}
Repeater {
id: rectRep
model: Math.max( Math.pow(2, control.model-1) * Math.pow(0.5, (index)), 2)
delegate: Item {
id: matchItm
property bool lowerPart: (index%2 > 0)
property int thisIndex: index
property var thisMatchData: control.allFlowchartData[ thisIsSemifinal ? roundCol.thisIndex+1 : roundCol.thisIndex][matchItm.thisIndex]
property bool thisIsFinal: roundCol.thisIsLastRound && thisIndex === rectRep.model - 2
property bool thisIsSemifinal: roundCol.thisIsLastRound && thisIndex === rectRep.model - 1
Component.onCompleted: {
if(thisIsSemifinal){
console.log("this is semi final")
console.log(thisMatchData)
}
console.log(thisMatchData)
}
height: !roundCol.thisIsLastRound ? (roundCol.height - roundNameLa.height) / rectRep.model - roundCol.spacing : (thisIsFinal ? (roundCol.height - roundNameLa.height) * 0.5 + control.tileSize * 0.5 : (roundCol.height - roundNameLa.height) - (roundCol.height - roundNameLa.height) * 0.5 + control.tileSize * 0.5 )
width: parent.width
Canvas {
id: lineCanvas
width: app.width; height: app.height
contextType: "2d"
visible: !roundCol.thisIsLastRound
property int targetX: matchItm.width + control.spacing
property int targetY: matchItm.lowerPart ? 0:matchItm.height
Path {
id: myPath
startX: matchRect.x + matchRect.width * matchRect.scale ; startY: matchRect.y + matchRect.height * 0.5
PathCurve { x: lineCanvas.targetX ; y: lineCanvas.targetY }
}
onPaint: {
context.strokeStyle = Qt.rgba(.4,.6,.8);
context.path = myPath;
context.stroke();
}
}
Rectangle {
id: matchRect
anchors {
centerIn: !matchItm.thisIsFinal ? parent:undefined
bottom: matchItm.thisIsFinal ? parent.bottom:undefined
}
//anchors.verticalCenterOffset: matchItm.lowerPart ? 10:10
width: parent.width
height: control.tileSize
//scale: 0.9
color: "white"
border.color: "lightgrey"
border.width: 1
radius: height * 0.2
Material.elevation: 10
Grid {
columns: app.landscape() ? 2:0
rows: app.landscape() ? 0:2
anchors.fill: parent
anchors.margins: matchRect.radius * 0.5
Repeater {
// for the two athletes
model: 2
delegate: Row {
height: app.landscape() ? parent.height:parent.height / 2
width: app.landscape() ? parent.width / 2 : parent.width
spacing: app.landscape() ? width * 0.1:height * 0.05
Label {
height: parent.height
width: parent.width * 0.2
verticalAlignment: Text.AlignVCenter
font.pixelSize: height * 0.6
fontSizeMode: Text.Fit
minimumPixelSize: 1
text: matchItm.thisMatchData[index]['result_rank0']
}
Label {
height: parent.height
width: parent.width * 0.5
verticalAlignment: Text.AlignVCenter
font.pixelSize: height * 0.6
fontSizeMode: Text.Fit
minimumPixelSize: 1
color: (parseInt(matchItm.thisMatchData[0]["result_rank" + roundCol.thisRound ]) < parseInt(matchItm.thisMatchData[1]["result_rank" + roundCol.thisRound])) === (index === 0 ? true:false) ? "green":"black"
text: matchItm.thisMatchData[index]['firstname'] + " " + matchItm.thisMatchData[index]['lastname']
}
/*Rectangle {
id: hasWonRect
anchors.verticalCenter: parent.verticalCenter
height: width
width: parent.width * 0.1
scale: 0.7
radius: width * 0.5
color: "green"
visible: (parseInt(matchItm.thisMatchData[0]["result_rank" + roundCol.thisRound ]) < parseInt(matchItm.thisMatchData[1]["result_rank" + roundCol.thisRound])) === (index === 0 ? true:false)
}*/
}
}
}
}
}
}
}
}