diff --git a/addons/majority_judgment/MajorityJudgmentCandidateMeritProfile.gd b/addons/majority_judgment/MajorityJudgmentCandidateMeritProfile.gd index 04100c4..aea7d3e 100644 --- a/addons/majority_judgment/MajorityJudgmentCandidateMeritProfile.gd +++ b/addons/majority_judgment/MajorityJudgmentCandidateMeritProfile.gd @@ -8,8 +8,6 @@ by putting on a single line the judgments they received, sorted by increasing (or decreasing) grade. 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 export(Array, int) var grades +export(Array, Color) var colors #export(int) var grades_amount @@ -30,6 +29,14 @@ func count_judgments() -> int: 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: """ 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]) 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 diff --git a/addons/majority_judgment/MajorityJudgmentCandidateTally.gd b/addons/majority_judgment/MajorityJudgmentCandidateTally.gd index 838cb56..2fce67e 100644 --- a/addons/majority_judgment/MajorityJudgmentCandidateTally.gd +++ b/addons/majority_judgment/MajorityJudgmentCandidateTally.gd @@ -10,3 +10,4 @@ export(int) var creation_timestamp:int export(Resource) var median_grade export(int) var position:int + diff --git a/addons/majority_judgment/MajorityJudgmentGrade.gd b/addons/majority_judgment/MajorityJudgmentGrade.gd index 0d9e32b..8a22f52 100644 --- a/addons/majority_judgment/MajorityJudgmentGrade.gd +++ b/addons/majority_judgment/MajorityJudgmentGrade.gd @@ -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, but let's go full oriented object. +Each grade will also handle its color, I think. - -PERHAPS ALREADY DEPRECATED +L10N PERHAPS ALREADY DEPRECATED --------------- Supports localization (l10n). Default language is Esperanto. Videoludemuloj! +----------------------------------------------- """ @@ -19,6 +21,7 @@ Default language is Esperanto. Videoludemuloj! 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 # 0: worst grade # more: better grade (up to gradation size minus one) diff --git a/addons/majority_judgment/MajorityJudgmentGrading.gd b/addons/majority_judgment/MajorityJudgmentGrading.gd index 43f5182..5670dd4 100644 --- a/addons/majority_judgment/MajorityJudgmentGrading.gd +++ b/addons/majority_judgment/MajorityJudgmentGrading.gd @@ -3,6 +3,7 @@ class_name MajorityJudgmentGrading """ An ordered list of grades, from worst to best. + It's VERY IMPORTANT that the grades be NON-AMBIGUOUS, that everyone would sort the grades in the same order if they were given shuffled. @@ -14,11 +15,48 @@ if they were given shuffled. 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): assert(_grades, "Provide an array of MajorityJudgmentGrade") grades = _grades for i in range(grades.size()): grades[i].worth = i + func get_grades() -> Array: 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] diff --git a/addons/majority_judgment/MajorityJudgmentPoll.gd b/addons/majority_judgment/MajorityJudgmentPoll.gd index 6b5fc10..5db5766 100644 --- a/addons/majority_judgment/MajorityJudgmentPoll.gd +++ b/addons/majority_judgment/MajorityJudgmentPoll.gd @@ -44,7 +44,13 @@ func _init(): # 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: @@ -82,6 +88,10 @@ func get_candidates() -> Array: return candidates +func has_judgments() -> bool: + return 0 < judgments.size() + + func set_judgments(__judgments:Array) -> void: judgments = __judgments @@ -106,6 +116,8 @@ func get_judgments() -> Array: func tally() -> MajorityJudgmentPollTally: + if not has_judgments(): + return null # Pick relevant tallier from settings later on var tallier = MajorityJudgmentEasyTallier.new() # var tallier = MajorityJudgmentLiquidTallier.new() @@ -119,8 +131,17 @@ func get_participants() -> Array: if not participants.has(participant): participants.append(participant) return participants - func count_participants() -> int: 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 + diff --git a/addons/majority_judgment/MajorityJudgmentPollTally.gd b/addons/majority_judgment/MajorityJudgmentPollTally.gd index bcec376..64b43db 100644 --- a/addons/majority_judgment/MajorityJudgmentPollTally.gd +++ b/addons/majority_judgment/MajorityJudgmentPollTally.gd @@ -19,3 +19,10 @@ export(Resource) var poll #:MajorityJudgmentPoll # so check each MajorityJudgmentCandidateTally.position. 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 diff --git a/addons/majority_judgment/nodes/MajorityJudgmentLinearGradeNode.tscn b/addons/majority_judgment/nodes/MajorityJudgmentLinearGradeNode.tscn new file mode 100644 index 0000000..d92d9cc --- /dev/null +++ b/addons/majority_judgment/nodes/MajorityJudgmentLinearGradeNode.tscn @@ -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 +} diff --git a/addons/majority_judgment/nodes/MajorityJudgmentLinearResultsControl.gd b/addons/majority_judgment/nodes/MajorityJudgmentLinearResultsControl.gd new file mode 100644 index 0000000..4367bbf --- /dev/null +++ b/addons/majority_judgment/nodes/MajorityJudgmentLinearResultsControl.gd @@ -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 diff --git a/addons/majority_judgment/nodes/MajorityJudgmentLinearResultsControl.svg b/addons/majority_judgment/nodes/MajorityJudgmentLinearResultsControl.svg new file mode 100644 index 0000000..02e2774 --- /dev/null +++ b/addons/majority_judgment/nodes/MajorityJudgmentLinearResultsControl.svg @@ -0,0 +1,78 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + diff --git a/addons/majority_judgment/nodes/MajorityJudgmentLinearResultsControl.svg.import b/addons/majority_judgment/nodes/MajorityJudgmentLinearResultsControl.svg.import new file mode 100644 index 0000000..b0a183f --- /dev/null +++ b/addons/majority_judgment/nodes/MajorityJudgmentLinearResultsControl.svg.import @@ -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 diff --git a/addons/majority_judgment/nodes/MajorityJudgmentLinearResultsControl.tscn b/addons/majority_judgment/nodes/MajorityJudgmentLinearResultsControl.tscn new file mode 100644 index 0000000..5470076 --- /dev/null +++ b/addons/majority_judgment/nodes/MajorityJudgmentLinearResultsControl.tscn @@ -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 ) diff --git a/addons/majority_judgment/nodes/MajorityJudgmentMeritProfileControl.gd b/addons/majority_judgment/nodes/MajorityJudgmentMeritProfileControl.gd new file mode 100644 index 0000000..fc7b83d --- /dev/null +++ b/addons/majority_judgment/nodes/MajorityJudgmentMeritProfileControl.gd @@ -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 + + + + + + diff --git a/addons/majority_judgment/nodes/MajorityJudgmentMeritProfileControl.tscn b/addons/majority_judgment/nodes/MajorityJudgmentMeritProfileControl.tscn new file mode 100644 index 0000000..2b6e441 --- /dev/null +++ b/addons/majority_judgment/nodes/MajorityJudgmentMeritProfileControl.tscn @@ -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="."] diff --git a/core/App.gd b/core/App.gd index f259473..c6f3ff7 100644 --- a/core/App.gd +++ b/core/App.gd @@ -39,5 +39,8 @@ func start_poll(poll): # Start listening to chat commands # Prepare timer for automatic closing of the poll # 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) diff --git a/gui/forms/NewPollForm.gd b/gui/forms/NewPollForm.gd index 93c7330..f65f8ac 100644 --- a/gui/forms/NewPollForm.gd +++ b/gui/forms/NewPollForm.gd @@ -18,7 +18,12 @@ func collect_poll_from_form(form:Control) -> MajorityJudgmentPoll: if 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