feat: implement an unordered but animated version of the linear results

The data is a dummy, waiting to be connected to a provider like Twitch.
We also need to sort the profiles, and animate the sort ;)

The GUI elements will probably need more work as well (like clipping, etc.)
master
Dominique Merle 4 years ago
parent 323a5fc345
commit 4e05835e81

@ -8,8 +8,6 @@ by putting on a single line the judgments they received,
sorted by increasing (or decreasing) grade. sorted by increasing (or decreasing) grade.
This is built by the Talliers. This is built by the Talliers.
Overzealous object-oriented approach yet again, perhaps.
""" """
@ -19,6 +17,7 @@ export(Resource) var candidate_tally #:MajorityJudgmentCandidateTally
# For each grade (worst to best), the amount of judgments received # For each grade (worst to best), the amount of judgments received
export(Array, int) var grades export(Array, int) var grades
export(Array, Color) var colors
#export(int) var grades_amount #export(int) var grades_amount
@ -30,6 +29,14 @@ func count_judgments() -> int:
return amount return amount
func count_grades_with_judgments() -> int:
var amount := 0
for amount_for_grade in self.grades:
if amount_for_grade:
amount += 1
return amount
func get_median() -> int: func get_median() -> int:
""" """
Returns the grade index, not a Grade instance. Returns the grade index, not a Grade instance.
@ -65,3 +72,10 @@ func remove_one_judgment(grade_index:int) -> void:
assert(0 < self.grades[grade_index]) assert(0 < self.grades[grade_index])
self.grades[grade_index] -= 1 self.grades[grade_index] -= 1
func get_color_of_grade(grade_index:int) -> Color:
if self.colors:
return self.colors[grade_index]
if self.candidate_tally and self.candidate_tally.poll:
return self.candidate_tally.poll.grading.get_color_of_grade(grade_index)
return Color.blue

@ -10,3 +10,4 @@ export(int) var creation_timestamp:int
export(Resource) var median_grade export(Resource) var median_grade
export(int) var position:int export(int) var position:int

@ -3,14 +3,16 @@ class_name MajorityJudgmentGrade
""" """
A single grade. A single grade, like 'Excellent' or 'To Reject'.
Could be just an int or a string, Could be just an int or a string,
but let's go full oriented object. but let's go full oriented object.
Each grade will also handle its color, I think.
L10N PERHAPS ALREADY DEPRECATED ---------------
PERHAPS ALREADY DEPRECATED
Supports localization (l10n). Supports localization (l10n).
Default language is Esperanto. Videoludemuloj! Default language is Esperanto. Videoludemuloj!
-----------------------------------------------
""" """
@ -19,6 +21,7 @@ Default language is Esperanto. Videoludemuloj!
export(Dictionary) var localized_names:Dictionary export(Dictionary) var localized_names:Dictionary
# (WiP: perhaps not used right now, but it is set)
# This is set by the gradation this grade is included in # This is set by the gradation this grade is included in
# 0: worst grade # 0: worst grade
# more: better grade (up to gradation size minus one) # more: better grade (up to gradation size minus one)

@ -3,6 +3,7 @@ class_name MajorityJudgmentGrading
""" """
An ordered list of grades, from worst to best. An ordered list of grades, from worst to best.
It's VERY IMPORTANT that the grades be NON-AMBIGUOUS, It's VERY IMPORTANT that the grades be NON-AMBIGUOUS,
that everyone would sort the grades in the same order that everyone would sort the grades in the same order
if they were given shuffled. if they were given shuffled.
@ -14,11 +15,48 @@ if they were given shuffled.
export(Array) var grades:Array setget set_grades, get_grades export(Array) var grades:Array setget set_grades, get_grades
static func make_empty():
return load("res://addons/majority_judgment/MajorityJudgmentGrading.gd").new()
static func make_quality_6():
var grading = make_empty()
grading.set_grades([
MajorityJudgmentGrade.make("To Reject"),
MajorityJudgmentGrade.make("Poor"),
MajorityJudgmentGrade.make("Passable"),
MajorityJudgmentGrade.make("Good"),
MajorityJudgmentGrade.make("Very Good"),
MajorityJudgmentGrade.make("Excellent"),
])
return grading
func set_grades(_grades:Array): func set_grades(_grades:Array):
assert(_grades, "Provide an array of MajorityJudgmentGrade") assert(_grades, "Provide an array of MajorityJudgmentGrade")
grades = _grades grades = _grades
for i in range(grades.size()): for i in range(grades.size()):
grades[i].worth = i grades[i].worth = i
func get_grades() -> Array: func get_grades() -> Array:
return grades return grades
func get_colors() -> Array:
assert(grades.size() == 6, "WIP")
return [
Color("#e0361c"),
Color("#ee6e00"),
Color("#fdb200"),
Color("#c6d700"),
Color("#7ec239"),
Color("#02ab58"),
# Color("#007e3d"),
]
func get_color_of_grade(grade_index:int):
var colors = get_colors()
grade_index = clamp(grade_index, 0, colors.size()-1)
return colors[grade_index]

@ -44,7 +44,13 @@ func _init():
# Instead, use a factory approach # Instead, use a factory approach
#static func make(__title, __grading): static func make(__title, __grading, __candidates):
var poll = load("res://addons/majority_judgment/MajorityJudgmentPoll.gd").new()
poll.set_title(__title)
poll.set_grading(__grading)
poll.set_candidates(__candidates)
return poll
func set_title(__title:String) -> void: func set_title(__title:String) -> void:
@ -82,6 +88,10 @@ func get_candidates() -> Array:
return candidates return candidates
func has_judgments() -> bool:
return 0 < judgments.size()
func set_judgments(__judgments:Array) -> void: func set_judgments(__judgments:Array) -> void:
judgments = __judgments judgments = __judgments
@ -106,6 +116,8 @@ func get_judgments() -> Array:
func tally() -> MajorityJudgmentPollTally: func tally() -> MajorityJudgmentPollTally:
if not has_judgments():
return null
# Pick relevant tallier from settings later on # Pick relevant tallier from settings later on
var tallier = MajorityJudgmentEasyTallier.new() var tallier = MajorityJudgmentEasyTallier.new()
# var tallier = MajorityJudgmentLiquidTallier.new() # var tallier = MajorityJudgmentLiquidTallier.new()
@ -119,8 +131,17 @@ func get_participants() -> Array:
if not participants.has(participant): if not participants.has(participant):
participants.append(participant) participants.append(participant)
return participants return participants
func count_participants() -> int: func count_participants() -> int:
return get_participants().size() return get_participants().size()
func get_dummy_merit_profile(candidate):
var merit = MajorityJudgmentCandidateMeritProfile.new()
merit.grades = Array()
for i in range(get_grading().grades.size()):
merit.grades.append(1)
merit.colors = get_grading().get_colors()
return merit

@ -19,3 +19,10 @@ export(Resource) var poll #:MajorityJudgmentPoll
# so check each MajorityJudgmentCandidateTally.position. # so check each MajorityJudgmentCandidateTally.position.
export(Array, Resource) var candidates_tallies:Array export(Array, Resource) var candidates_tallies:Array
func get_tally_of_candidate(candidate:MajorityJudgmentCandidate) -> MajorityJudgmentCandidateTally:
for candidate_tally in self.candidates_tallies:
if candidate_tally.candidate == candidate:
return candidate_tally
assert(false, "Candidate tally not found.")
return MajorityJudgmentCandidateTally.new() # urgh

@ -0,0 +1,8 @@
[gd_scene format=2]
[node name="MajorityJudgmentLinearGradeNode" type="ColorRect"]
margin_right = 40.0
margin_bottom = 40.0
__meta__ = {
"_edit_use_anchors_": false
}

@ -0,0 +1,180 @@
extends Control
const MeritProfileScene = preload("res://addons/majority_judgment/nodes/MajorityJudgmentMeritProfileControl.tscn")
export(Resource) var poll setget set_poll, get_poll
#func _ready():
# set_poll(load("res://test_poll.tres"))
func get_poll() -> MajorityJudgmentPoll:
return poll
func set_poll(__poll:MajorityJudgmentPoll):
poll = __poll
craft_nodes()
update_nodes()
yield(get_tree().create_timer(3), "timeout")
var j = MajorityJudgmentJudgment.new()
j.set_participant(MajorityJudgmentParticipant.make("Plume"))
j.set_grade(poll.grading.grades[2])
j.set_candidate(poll.candidates[0])
poll.add_judgment(j)
var sylvain = MajorityJudgmentParticipant.make("Sylvain")
j = MajorityJudgmentJudgment.new()
j.set_participant(sylvain)
j.set_grade(poll.grading.grades[3])
j.set_candidate(poll.candidates[0])
poll.add_judgment(j)
j = MajorityJudgmentJudgment.new()
j.set_participant(sylvain)
j.set_grade(poll.grading.grades[3])
j.set_candidate(poll.candidates[1])
poll.add_judgment(j)
j = MajorityJudgmentJudgment.new()
j.set_participant(sylvain)
j.set_grade(poll.grading.grades[4])
j.set_candidate(poll.candidates[2])
poll.add_judgment(j)
j = MajorityJudgmentJudgment.new()
j.set_participant(MajorityJudgmentParticipant.make("Sabre"))
j.set_grade(poll.grading.grades[5])
j.set_candidate(poll.candidates[0])
poll.add_judgment(j)
update_nodes()
yield(get_tree().create_timer(2), "timeout")
var tiger = MajorityJudgmentParticipant.make("Tiger")
j = MajorityJudgmentJudgment.new()
j.set_participant(tiger)
j.set_grade(poll.grading.grades[4])
j.set_candidate(poll.candidates[0])
poll.add_judgment(j)
update_nodes()
yield(get_tree().create_timer(2), "timeout")
j = MajorityJudgmentJudgment.new()
j.set_participant(tiger)
j.set_grade(poll.grading.grades[1])
j.set_candidate(poll.candidates[2])
poll.add_judgment(j)
update_nodes()
var profiles_nodes := Array()
func craft_nodes():
var tally : MajorityJudgmentPollTally = get_poll().tally()
var candidate_index = 0 # as they were initially written, before the sort
for candidate in get_poll().get_candidates():
# var candidate_tally := tally.get_tally_of_candidate(candidate)
# var merit_profile = candidate_tally.merit_profile
var profile = create_merit_profile_scene(6)
if tally:
profile.refresh(tally.get_tally_of_candidate(candidate).merit_profile)
var height = profile.compute_height()
# profile.margin_top = candidate_index * 50
# profile.margin_left = 0
# profile.margin_right = 0
# profile.margin_bottom = candidate_index * 50 + 42
profile.rect_min_size = Vector2(400, height)
var container = $CenterContainer/ProfilesContainer
var wrapper = HBoxContainer.new()
var candidate_label = Label.new()
candidate_label.text = candidate.get_name()
candidate_label.size_flags_horizontal = Control.SIZE_EXPAND_FILL
# candidate_label.size_flags_vertical = Control.SIZE_EXPAND_FILL
candidate_label.rect_min_size = Vector2(70, height)
candidate_label.valign = Label.VALIGN_CENTER
candidate_label.align = Label.ALIGN_RIGHT
wrapper.add_child(candidate_label)
wrapper.add_child(profile)
container.add_child(wrapper)
profiles_nodes.append(profile)
candidate_index += 1
func update_nodes():
var tally : MajorityJudgmentPollTally = get_poll().tally()
var candidate_index = 0 # as they were initially written, before the sort
for candidate in get_poll().get_candidates():
# var candidate_tally := tally.get_tally_of_candidate(candidate)
# var merit_profile = candidate_tally.merit_profile
var profile = profiles_nodes[candidate_index]
if tally:
var merit = tally.get_tally_of_candidate(candidate).merit_profile
profile.refresh(merit)
else:
var merit = poll.get_dummy_merit_profile(candidate)
profile.refresh(merit)
candidate_index += 1
#func test():
# var poll : MajorityJudgmentPoll = MajorityJudgmentPoll.make(
# "Test",
# MajorityJudgmentGrading.make_quality_6(),
# [
# MajorityJudgmentCandidate.make("Candidate A"),
# MajorityJudgmentCandidate.make("Candidate B"),
# MajorityJudgmentCandidate.make("Candidate C"),
# ]
# )
#
# var poll_tally = MajorityJudgmentPollTally.new()
# poll_tally.poll = poll
# var candidate_tallies = Array()
# var candidate_tally := MajorityJudgmentCandidateTally.new()
# candidate_tally.poll = poll
# candidate_tally.merit_profile = MajorityJudgmentCandidateMeritProfile.new()
# candidate_tally.merit_profile.grades = [
# 10,
# 2,
# 5,
# 7,
# 1,
# 11,
# ]
# candidate_tally.merit_profile.candidate_tally = candidate_tally
# candidate_tallies.append(candidate_tally)
# create_merit_profile_scene(candidate_tally.merit_profile)
#
## candidate_tally = MajorityJudgmentCandidateTally.new()
## candidate_tally.merit_profile = MajorityJudgmentCandidateMeritProfile.new()
## candidate_tally.merit_profile.grades = [
## 2,
## 7,
## 10,
## 11,
## 1,
## 5,
## ]
## candidate_tallies.append(candidate_tally)
## create_merit_profile_scene(candidate_tally.merit_profile)
#
# poll_tally.candidates_tallies = candidate_tallies
func create_merit_profile_scene(gradation_size:int):
var mps = MeritProfileScene.instance()
mps.set_poll(get_poll())
mps.craft_nodes(gradation_size)
return mps

@ -0,0 +1,78 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="16"
height="16"
viewBox="0 0 16 16"
id="svg2"
version="1.1"
inkscape:version="0.91 r13725"
inkscape:export-filename="/home/djrm/Projects/godot/tools/editor/icons/icon_node.png"
inkscape:export-xdpi="90"
inkscape:export-ydpi="90"
sodipodi:docname="icon_node.svg">
<defs
id="defs4" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="22.627416"
inkscape:cx="4.1094292"
inkscape:cy="4.5020156"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="true"
units="px"
inkscape:snap-bbox="true"
inkscape:bbox-paths="true"
inkscape:bbox-nodes="true"
inkscape:snap-bbox-edge-midpoints="true"
inkscape:snap-bbox-midpoints="true"
inkscape:snap-object-midpoints="true"
inkscape:snap-center="true"
inkscape:window-width="1920"
inkscape:window-height="1016"
inkscape:window-x="0"
inkscape:window-y="27"
inkscape:window-maximized="1">
<inkscape:grid
type="xygrid"
id="grid3336"
empspacing="4" />
</sodipodi:namedview>
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-1036.3622)">
<path
style="opacity:1;fill:#e0e0e0;fill-opacity:1;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
d="M 8 2 A 6 6 0 0 0 2 8 A 6 6 0 0 0 8 14 A 6 6 0 0 0 14 8 A 6 6 0 0 0 8 2 z M 8 4 A 4 4 0 0 1 12 8 A 4 4 0 0 1 8 12 A 4 4 0 0 1 4 8 A 4 4 0 0 1 8 4 z "
transform="translate(0,1036.3622)"
id="path4154" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.5 KiB

@ -0,0 +1,34 @@
[remap]
importer="texture"
type="StreamTexture"
path="res://.import/MajorityJudgmentLinearResultsControl.svg-fa6c8a91c72c88a932e738e8ceee0654.stex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/majority_judgment/nodes/MajorityJudgmentLinearResultsControl.svg"
dest_files=[ "res://.import/MajorityJudgmentLinearResultsControl.svg-fa6c8a91c72c88a932e738e8ceee0654.stex" ]
[params]
compress/mode=0
compress/lossy_quality=0.7
compress/hdr_mode=0
compress/bptc_ldr=0
compress/normal_map=0
flags/repeat=0
flags/filter=true
flags/mipmaps=false
flags/anisotropic=false
flags/srgb=2
process/fix_alpha_border=true
process/premult_alpha=false
process/HDR_as_SRGB=false
process/invert_color=false
stream=false
size_limit=0
detect_3d=true
svg/scale=1.0

@ -0,0 +1,22 @@
[gd_scene load_steps=2 format=2]
[ext_resource path="res://addons/majority_judgment/nodes/MajorityJudgmentLinearResultsControl.gd" type="Script" id=1]
[node name="MajorityJudgmentLinearResultsControl" type="Control"]
anchor_right = 1.0
anchor_bottom = 1.0
script = ExtResource( 1 )
__meta__ = {
"_edit_use_anchors_": false
}
[node name="CenterContainer" type="CenterContainer" parent="."]
anchor_right = 1.0
anchor_bottom = 1.0
[node name="ProfilesContainer" type="VBoxContainer" parent="CenterContainer"]
margin_left = 312.0
margin_top = 99.0
margin_right = 712.0
margin_bottom = 499.0
rect_min_size = Vector2( 400, 400 )

@ -0,0 +1,113 @@
extends Control
const GradeNode = preload("res://addons/majority_judgment/nodes/MajorityJudgmentLinearGradeNode.tscn")
export(Resource) var poll setget set_poll, get_poll # MajorityJudgmentPoll
#export(Resource) var merit_profile # MajorityJudgmentCandidateMeritProfile
export var gap_size := 4 # pixels
export var total_width := 300 # pixels
var _grades_nodes := Array()
#func _ready():
# assert(self.merit_profile, "Inject a merit profile first")
# craft_nodes()
# refresh()
func set_poll(__poll:MajorityJudgmentPoll) -> void:
poll = __poll
func get_poll() -> MajorityJudgmentPoll:
return poll
func craft_nodes(grading_size):
assert(0 == _grades_nodes.size())
# or queue_free() them
for grade_index in range(grading_size):
var grade_node = GradeNode.instance()
grade_node.name = "GradeNode%d" % [grade_index]
grade_node.color = get_poll().get_grading().get_color_of_grade(grade_index)
add_child(grade_node)
_grades_nodes.append(grade_node)
func compute_height():
var gold = 2.0 / (sqrt(5.0) + 1.0)
assert(1.0/gold == 1.0+gold)
var amount_of_grades : int = poll.grading.grades.size()
return int(round(total_width / float(amount_of_grades)) * gold)
func refresh(merit_profile:MajorityJudgmentCandidateMeritProfile):
$Tween.remove_all()
var total_amount_of_judgments : int = merit_profile.count_judgments()
var amount_of_grades_with_judgments : int = merit_profile.count_grades_with_judgments()
var amount_of_grades : int = merit_profile.grades.size()
var height : int = compute_height()
var cursor := 0
for grade_index in range(amount_of_grades):
var grade_node = _grades_nodes[grade_index]
var amount_of_judgments_for_grade = merit_profile.grades[grade_index]
var width = (
(total_width - (amount_of_grades_with_judgments-1) * gap_size)
*
amount_of_judgments_for_grade
/
total_amount_of_judgments
)
var grade_margin_right = stepify(cursor + width, total_width / total_amount_of_judgments)
if 0 == width:
grade_margin_right = cursor
var tween_transition = Tween.TRANS_SINE
var tween_ease = Tween.EASE_IN_OUT
var tween_duration = 0.9
var tween_delay = 0
$Tween.interpolate_property(
grade_node,
"margin_left",
grade_node.margin_left,
cursor,
tween_duration,
tween_transition,
tween_ease,
tween_delay
)
$Tween.interpolate_property(
grade_node,
"margin_right",
grade_node.margin_right,
# cursor + width,
grade_margin_right,
tween_duration,
tween_transition,
tween_ease,
tween_delay
)
$Tween.start()
grade_node.margin_top = 0
grade_node.margin_bottom = height
# grade_node.margin_left = cursor
# grade_node.margin_right = cursor + width
if width:
# cursor = width + gap_size
cursor = grade_margin_right + gap_size

@ -0,0 +1,13 @@
[gd_scene load_steps=2 format=2]
[ext_resource path="res://addons/majority_judgment/nodes/MajorityJudgmentMeritProfileControl.gd" type="Script" id=1]
[node name="MajorityJudgmentMeritProfileControl" type="Control"]
anchor_right = 1.0
anchor_bottom = 1.0
script = ExtResource( 1 )
__meta__ = {
"_edit_use_anchors_": false
}
[node name="Tween" type="Tween" parent="."]

@ -39,5 +39,8 @@ func start_poll(poll):
# Start listening to chat commands # Start listening to chat commands
# Prepare timer for automatic closing of the poll # Prepare timer for automatic closing of the poll
# Move to poll results scene # Move to poll results scene
var _done = get_tree().change_scene("res://addons/majority_judgment/nodes/MajorityJudgmentLinearResultsControl.tscn")
yield(get_tree(), "idle_frame")
get_tree().current_scene.set_poll(poll)

@ -18,7 +18,12 @@ func collect_poll_from_form(form:Control) -> MajorityJudgmentPoll:
if title_line_edit.text: if title_line_edit.text:
poll.set_title(title_line_edit.text) poll.set_title(title_line_edit.text)
# var candidates_tree = form.find_node("CandidatesTree", true, false) var candidates_tree = form.find_node("CandidatesTree", true, false)
var candidate_item = candidates_tree.get_root().get_children()
while (candidate_item):
poll.add_candidate(MajorityJudgmentCandidate.make(candidate_item.get_text(0)))
candidate_item = candidate_item.get_next()
poll.set_grading(MajorityJudgmentGrading.make_quality_6())
return poll return poll

Loading…
Cancel
Save