From d58635bb3501b49221186e54fef79bf596d83cc0 Mon Sep 17 00:00:00 2001 From: Fyodor Sobolev <117388856+fsobolev@users.noreply.github.com> Date: Sun, 29 Jan 2023 17:15:05 +0300 Subject: [PATCH 01/19] Add circle option --- data/io.github.fsobolev.Cavalier.gschema.xml | 5 ++++ src/drawing_area.py | 1 + src/preferences_window.py | 27 ++++++++++++++++++++ 3 files changed, 33 insertions(+) diff --git a/data/io.github.fsobolev.Cavalier.gschema.xml b/data/io.github.fsobolev.Cavalier.gschema.xml index 3e9acc3..82b15c4 100644 --- a/data/io.github.fsobolev.Cavalier.gschema.xml +++ b/data/io.github.fsobolev.Cavalier.gschema.xml @@ -37,6 +37,11 @@ "wave" + + Circle + Whether to modify drawing modes to draw in circle + false + Drawing area margin Size of gaps around drawing area (in pixels). diff --git a/src/drawing_area.py b/src/drawing_area.py index ade9fae..5b939d4 100644 --- a/src/drawing_area.py +++ b/src/drawing_area.py @@ -64,6 +64,7 @@ def run(self): def on_settings_changed(self, key): self.draw_mode = self.settings['mode'] + self.circle = self.settings['circle'] self.set_margin_top(self.settings['margin']) self.set_margin_bottom(self.settings['margin']) self.set_margin_start(self.settings['margin']) diff --git a/src/preferences_window.py b/src/preferences_window.py index d303324..a897506 100644 --- a/src/preferences_window.py +++ b/src/preferences_window.py @@ -100,6 +100,26 @@ def create_cavalier_page(self): self.bars_row.set_activatable_widget(self.bars_check_btn) self.cavalier_mode_group.add(self.bars_row) + self.mode_variant_stack = Gtk.Stack.new() + self.mode_variant_stack.set_margin_top(24) + self.cavalier_mode_group.add(self.mode_variant_stack) + + self.box_group = Adw.PreferencesGroup.new() + self.mode_variant_stack.add_titled(self.box_group, 'box', _('Box')) + self.mirror_row = Adw.ActionRow.new() + self.mirror_row.set_title(_('Mirror')) + + self.circle_group = Adw.PreferencesGroup.new() + self.mode_variant_stack.add_titled(self.circle_group, 'circle', \ + _('Circle')) + self.radius_row = Adw.ActionRow.new() + self.radius_row.set_title(_('Radius')) + self.circle_group.add(self.radius_row) + + self.circle_switcher = Gtk.StackSwitcher.new() + self.circle_switcher.set_stack(self.mode_variant_stack) + self.cavalier_mode_group.set_header_suffix(self.circle_switcher) + self.cavalier_group = Adw.PreferencesGroup.new() self.cavalier_page.add(self.cavalier_group) @@ -412,6 +432,8 @@ def load_settings(self): self.particles_check_btn, self.spine_check_btn, self.bars_check_btn)[ \ self.settings.get_range('mode')[1].index(self.settings['mode']) \ ].set_active(True) + self.mode_variant_stack.set_visible_child_name( \ + 'circle' if self.settings['circle'] else 'box') self.pref_margin_scale.set_value(self.settings['margin']) self.pref_offset_scale.set_value(self.settings['items-offset']) self.pref_roundness_scale.set_value( \ @@ -471,6 +493,11 @@ def bind_settings(self): 'particles') self.spine_check_btn.connect('toggled', self.change_mode, 'spine') self.bars_check_btn.connect('toggled', self.change_mode, 'bars') + # `notify::visible-child` signal returns additional parameter that + # we don't need, that's why lambda is used. + self.mode_variant_stack.connect('notify::visible-child', \ + lambda *args: self.save_setting(self.mode_variant_stack, 'circle', \ + self.mode_variant_stack.get_visible_child_name() == 'circle')) self.pref_margin_scale.connect('value-changed', self.save_setting, \ 'margin', self.pref_margin_scale.get_value) self.pref_offset_scale.connect('value-changed', self.save_setting, \ From 61afd5de9290595cdd5dd0265e9cde0afc49e10d Mon Sep 17 00:00:00 2001 From: Fyodor Sobolev <117388856+fsobolev@users.noreply.github.com> Date: Sun, 29 Jan 2023 19:04:33 +0300 Subject: [PATCH 02/19] Implement Line mode Circle variant --- src/drawing_area.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/drawing_area.py b/src/drawing_area.py index 5b939d4..183e929 100644 --- a/src/drawing_area.py +++ b/src/drawing_area.py @@ -31,7 +31,7 @@ from gi.repository import Gtk, GObject from threading import Thread from cavalier.cava import Cava -from cavalier.draw_functions import wave, levels, particles, spine, bars +from cavalier.draw_functions import * from cavalier.settings import CavalierSettings class CavalierDrawingArea(Gtk.DrawingArea): From bd130348cccbeaad24b839524bc935bfc0e15e41 Mon Sep 17 00:00:00 2001 From: Fyodor Sobolev <117388856+fsobolev@users.noreply.github.com> Date: Tue, 31 Jan 2023 03:30:31 +0300 Subject: [PATCH 03/19] Implement radius setting --- data/io.github.fsobolev.Cavalier.gschema.xml | 6 ++++++ src/drawing_area.py | 1 + src/preferences_window.py | 16 +++++++++++++--- 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/data/io.github.fsobolev.Cavalier.gschema.xml b/data/io.github.fsobolev.Cavalier.gschema.xml index 82b15c4..14eaa26 100644 --- a/data/io.github.fsobolev.Cavalier.gschema.xml +++ b/data/io.github.fsobolev.Cavalier.gschema.xml @@ -42,6 +42,12 @@ Whether to modify drawing modes to draw in circle false + + Radius + Radius of base circle (in percent) + + 20 + Drawing area margin Size of gaps around drawing area (in pixels). diff --git a/src/drawing_area.py b/src/drawing_area.py index 183e929..abad1b6 100644 --- a/src/drawing_area.py +++ b/src/drawing_area.py @@ -65,6 +65,7 @@ def run(self): def on_settings_changed(self, key): self.draw_mode = self.settings['mode'] self.circle = self.settings['circle'] + self.radius = self.settings['radius'] self.set_margin_top(self.settings['margin']) self.set_margin_bottom(self.settings['margin']) self.set_margin_start(self.settings['margin']) diff --git a/src/preferences_window.py b/src/preferences_window.py index a897506..ec7ce6d 100644 --- a/src/preferences_window.py +++ b/src/preferences_window.py @@ -112,9 +112,16 @@ def create_cavalier_page(self): self.circle_group = Adw.PreferencesGroup.new() self.mode_variant_stack.add_titled(self.circle_group, 'circle', \ _('Circle')) - self.radius_row = Adw.ActionRow.new() - self.radius_row.set_title(_('Radius')) - self.circle_group.add(self.radius_row) + self.pref_radius = Adw.ActionRow.new() + self.pref_radius.set_title(_('Radius')) + self.pref_radius.set_subtitle(_('Radius of base circle (in percent)')) + self.pref_radius_scale = Gtk.Scale.new_with_range( \ + Gtk.Orientation.HORIZONTAL, 0.0, 50.0, 1.0) + self.pref_radius_scale.set_size_request(180, -1) + self.pref_radius_scale.set_draw_value(True) + self.pref_radius_scale.set_value_pos(Gtk.PositionType.LEFT) + self.pref_radius.add_suffix(self.pref_radius_scale) + self.circle_group.add(self.pref_radius) self.circle_switcher = Gtk.StackSwitcher.new() self.circle_switcher.set_stack(self.mode_variant_stack) @@ -434,6 +441,7 @@ def load_settings(self): ].set_active(True) self.mode_variant_stack.set_visible_child_name( \ 'circle' if self.settings['circle'] else 'box') + self.pref_radius_scale.set_value(self.settings['radius']) self.pref_margin_scale.set_value(self.settings['margin']) self.pref_offset_scale.set_value(self.settings['items-offset']) self.pref_roundness_scale.set_value( \ @@ -498,6 +506,8 @@ def bind_settings(self): self.mode_variant_stack.connect('notify::visible-child', \ lambda *args: self.save_setting(self.mode_variant_stack, 'circle', \ self.mode_variant_stack.get_visible_child_name() == 'circle')) + self.pref_radius_scale.connect('value-changed', self.save_setting, \ + 'radius', self.pref_radius_scale.get_value) self.pref_margin_scale.connect('value-changed', self.save_setting, \ 'margin', self.pref_margin_scale.get_value) self.pref_offset_scale.connect('value-changed', self.save_setting, \ From b8c1a9fd47f7479f1805fd403d1643145f45b1f3 Mon Sep 17 00:00:00 2001 From: Fyodor Sobolev <117388856+fsobolev@users.noreply.github.com> Date: Tue, 31 Jan 2023 04:02:49 +0300 Subject: [PATCH 04/19] Radial gradient --- src/draw_functions.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/draw_functions.py b/src/draw_functions.py index f198166..9dc560a 100644 --- a/src/draw_functions.py +++ b/src/draw_functions.py @@ -44,6 +44,18 @@ def set_source(cr, height, colors, offset=0): (red, green, blue, alpha) = colors[0] cr.set_source_rgba(red / 255, green / 255, blue / 255, alpha) +def set_source_radial(cr, x, y, r0, r1, colors): + if len(colors) > 1: + pat = cairo.RadialGradient(x, y, r0, x, y, r1) + for i in range(len(colors)): + (red, green, blue, alpha) = colors[i] + pat.add_color_stop_rgba(1 / (i + 1), \ + red / 255, green / 255, blue / 255, alpha) + cr.set_source(pat) + else: + (red, green, blue, alpha) = colors[0] + cr.set_source_rgba(red / 255, green / 255, blue / 255, alpha) + def draw_element(cr, x, y, width, height, radius): degrees = math.pi / 180.0 cr.new_sub_path() From a14206c3c690e243f14cfee2b66427403820cbbf Mon Sep 17 00:00:00 2001 From: Fyodor Sobolev <117388856+fsobolev@users.noreply.github.com> Date: Tue, 31 Jan 2023 05:06:06 +0300 Subject: [PATCH 05/19] Wave mode Circle variant --- src/draw_functions.py | 53 +++++++++++++++++++++++++++++++++++++++++++ src/drawing_area.py | 8 +++++-- 2 files changed, 59 insertions(+), 2 deletions(-) diff --git a/src/draw_functions.py b/src/draw_functions.py index 9dc560a..92e8dc6 100644 --- a/src/draw_functions.py +++ b/src/draw_functions.py @@ -86,6 +86,59 @@ def wave(sample, cr, width, height, colors, fill, thickness): cr.set_line_width(thickness) cr.stroke() +def wave_circle(sample, cr, width, height, colors, radius, thickness): + ls = len(sample) + cr.move_to(width / 2, height / 2) + min_radius = min(width, height) * radius / 200 + max_radius = min(width, height) / 2 + set_source_radial(cr, width / 2, height / 2, min_radius, \ + max_radius + thickness, colors) + cr.rectangle(0, 0, width, height) + cr.arc_negative(width / 2, height / 2, min_radius - thickness / 2, 2 * math.pi, 0) + cr.clip() + cr.new_path() + cr.move_to(width / 2 + min_radius, height / 2) + cr.arc(width / 2, height / 2, min_radius, 0, 2 * math.pi) + cr.set_line_width(thickness) + cr.stroke() + cr.move_to( \ + width / 2 + math.cos(2 * math.pi / ls * (ls - 0.5) - 0.5 * math.pi) * \ + (min_radius + sample[-1] * (max_radius - min_radius)), \ + height / 2 + math.sin(2 * math.pi / ls * (ls - 0.5) - 0.5 * math.pi) * \ + (min_radius + sample[-1] * (max_radius - min_radius)) + ) + cr.curve_to( \ + width / 2 + math.cos(2 * math.pi / ls * (0) - 0.5 * math.pi) * \ + (min_radius + sample[-1] * (max_radius - min_radius)), \ + height / 2 + math.sin(2 * math.pi / ls * (0) - 0.5 * math.pi) * \ + (min_radius + sample[-1] * (max_radius - min_radius)), \ + width / 2 + math.cos(2 * math.pi / ls * (0) - 0.5 * math.pi) * \ + (min_radius + sample[0] * (max_radius - min_radius)), \ + height / 2 + math.sin(2 * math.pi / ls * (0) - 0.5 * math.pi) * \ + (min_radius + sample[0] * (max_radius - min_radius)), \ + width / 2 + math.cos(2 * math.pi / ls * (0.5) - 0.5 * math.pi) * \ + (min_radius + sample[0] * (max_radius - min_radius)), \ + height / 2 + math.sin(2 * math.pi / ls * (0.5) - 0.5 * math.pi) * \ + (min_radius + sample[0] * (max_radius - min_radius)) + ) + for i in range(ls - 1): + cr.curve_to( \ + width / 2 + math.cos(2 * math.pi / ls * (i + 1) - 0.5 * math.pi) * \ + (min_radius + sample[i] * (max_radius - min_radius)), \ + height / 2 + math.sin(2 * math.pi / ls * (i + 1) - 0.5 * math.pi) * \ + (min_radius + sample[i] * (max_radius - min_radius)), \ + width / 2 + math.cos(2 * math.pi / ls * (i + 1) - 0.5 * math.pi) * \ + (min_radius + sample[i+1] * (max_radius - min_radius)), \ + height / 2 + math.sin(2 * math.pi / ls * (i + 1) - 0.5 * math.pi) * \ + (min_radius + sample[i+1] * (max_radius - min_radius)), \ + width / 2 + math.cos(2 * math.pi / ls * (i + 1.5) - 0.5 * math.pi) * \ + (min_radius + sample[i+1] * (max_radius - min_radius)), \ + height / 2 + math.sin(2 * math.pi / ls * (i + 1.5) - 0.5 * math.pi) * \ + (min_radius + sample[i+1] * (max_radius - min_radius)) + ) + cr.close_path() # required to avoid artifact with thick lines + cr.fill() + def levels(sample, cr, width, height, colors, offset, radius, fill, thickness): set_source(cr, height, colors) ls = len(sample) diff --git a/src/drawing_area.py b/src/drawing_area.py index abad1b6..35cce8a 100644 --- a/src/drawing_area.py +++ b/src/drawing_area.py @@ -100,8 +100,12 @@ def on_settings_changed(self, key): def draw_func(self, area, cr, width, height, data, n): if len(self.cava_sample) > 0: if self.draw_mode == 'wave': - wave(self.cava_sample, cr, width, height, self.colors, \ - self.fill, self.thickness) + if self.circle: + wave_circle(self.cava_sample, cr, width, height, \ + self.colors, self.radius, self.thickness) + else: + wave(self.cava_sample, cr, width, height, self.colors, \ + self.fill, self.thickness) elif self.draw_mode == 'levels': levels(self.cava_sample, cr, width, height, self.colors, \ self.offset, self.roundness, self.fill, self.thickness) From dfd18fdefe6017be4fb3ef422f0d70f144cfd33a Mon Sep 17 00:00:00 2001 From: Fyodor Sobolev <117388856+fsobolev@users.noreply.github.com> Date: Thu, 2 Feb 2023 04:03:32 +0300 Subject: [PATCH 06/19] Wave Circle Fill Option --- data/io.github.fsobolev.Cavalier.gschema.xml | 5 +++++ src/draw_functions.py | 19 ++++++++++--------- src/drawing_area.py | 4 +++- src/preferences_window.py | 13 +++++++++++++ 4 files changed, 31 insertions(+), 10 deletions(-) diff --git a/data/io.github.fsobolev.Cavalier.gschema.xml b/data/io.github.fsobolev.Cavalier.gschema.xml index 14eaa26..b1a4ab7 100644 --- a/data/io.github.fsobolev.Cavalier.gschema.xml +++ b/data/io.github.fsobolev.Cavalier.gschema.xml @@ -42,6 +42,11 @@ Whether to modify drawing modes to draw in circle false + + Wave - Fill Circle + Whether to fill the inner circle when using Wave mode Circle variant. + false + Radius Radius of base circle (in percent) diff --git a/src/draw_functions.py b/src/draw_functions.py index 92e8dc6..c1bcf88 100644 --- a/src/draw_functions.py +++ b/src/draw_functions.py @@ -86,21 +86,22 @@ def wave(sample, cr, width, height, colors, fill, thickness): cr.set_line_width(thickness) cr.stroke() -def wave_circle(sample, cr, width, height, colors, radius, thickness): +def wave_circle(sample, cr, width, height, colors, radius, thickness, fill): ls = len(sample) cr.move_to(width / 2, height / 2) min_radius = min(width, height) * radius / 200 max_radius = min(width, height) / 2 set_source_radial(cr, width / 2, height / 2, min_radius, \ max_radius + thickness, colors) - cr.rectangle(0, 0, width, height) - cr.arc_negative(width / 2, height / 2, min_radius - thickness / 2, 2 * math.pi, 0) - cr.clip() - cr.new_path() - cr.move_to(width / 2 + min_radius, height / 2) - cr.arc(width / 2, height / 2, min_radius, 0, 2 * math.pi) - cr.set_line_width(thickness) - cr.stroke() + if not fill: + cr.rectangle(0, 0, width, height) + cr.arc_negative(width / 2, height / 2, min_radius - thickness / 2, 2 * math.pi, 0) + cr.clip() + cr.new_path() + cr.move_to(width / 2 + min_radius, height / 2) + cr.arc(width / 2, height / 2, min_radius, 0, 2 * math.pi) + cr.set_line_width(thickness) + cr.stroke() cr.move_to( \ width / 2 + math.cos(2 * math.pi / ls * (ls - 0.5) - 0.5 * math.pi) * \ (min_radius + sample[-1] * (max_radius - min_radius)), \ diff --git a/src/drawing_area.py b/src/drawing_area.py index 35cce8a..2db0c51 100644 --- a/src/drawing_area.py +++ b/src/drawing_area.py @@ -65,6 +65,7 @@ def run(self): def on_settings_changed(self, key): self.draw_mode = self.settings['mode'] self.circle = self.settings['circle'] + self.wave_circle_fill = self.settings['wave-circle-fill'] self.radius = self.settings['radius'] self.set_margin_top(self.settings['margin']) self.set_margin_bottom(self.settings['margin']) @@ -102,7 +103,8 @@ def draw_func(self, area, cr, width, height, data, n): if self.draw_mode == 'wave': if self.circle: wave_circle(self.cava_sample, cr, width, height, \ - self.colors, self.radius, self.thickness) + self.colors, self.radius, self.thickness, \ + self.wave_circle_fill) else: wave(self.cava_sample, cr, width, height, self.colors, \ self.fill, self.thickness) diff --git a/src/preferences_window.py b/src/preferences_window.py index ec7ce6d..f6894a4 100644 --- a/src/preferences_window.py +++ b/src/preferences_window.py @@ -66,6 +66,13 @@ def create_cavalier_page(self): self.wave_check_btn = Gtk.CheckButton.new() self.wave_row.add_prefix(self.wave_check_btn) self.wave_row.set_activatable_widget(self.wave_check_btn) + self.wave_circle_fill_box = Gtk.Box.new(Gtk.Orientation.HORIZONTAL, 10) + self.wave_row.add_suffix(self.wave_circle_fill_box) + self.wave_circle_fill_label = Gtk.Label.new(_('Fill completely')) + self.wave_circle_fill_box.append(self.wave_circle_fill_label) + self.wave_circle_fill_switch = Gtk.Switch.new() + self.wave_circle_fill_switch.set_valign(Gtk.Align.CENTER) + self.wave_circle_fill_box.append(self.wave_circle_fill_switch) self.cavalier_mode_group.add(self.wave_row) self.levels_row = Adw.ActionRow.new() @@ -441,6 +448,9 @@ def load_settings(self): ].set_active(True) self.mode_variant_stack.set_visible_child_name( \ 'circle' if self.settings['circle'] else 'box') + self.wave_circle_fill_box.set_visible(self.settings['circle']) + self.wave_circle_fill_switch.set_active( \ + self.settings['wave-circle-fill']) self.pref_radius_scale.set_value(self.settings['radius']) self.pref_margin_scale.set_value(self.settings['margin']) self.pref_offset_scale.set_value(self.settings['items-offset']) @@ -496,6 +506,9 @@ def load_settings(self): def bind_settings(self): self.wave_check_btn.connect('toggled', self.change_mode, 'wave') + self.wave_circle_fill_switch.connect('notify::state', \ + lambda *args : self.save_setting(self.wave_circle_fill_switch, \ + 'wave-circle-fill', self.wave_circle_fill_switch.get_state())) self.levels_check_btn.connect('toggled', self.change_mode, 'levels') self.particles_check_btn.connect('toggled', self.change_mode, \ 'particles') From 721b0e59131d8935978f3ea0d8b1579253b3dfc4 Mon Sep 17 00:00:00 2001 From: Fyodor Sobolev <117388856+fsobolev@users.noreply.github.com> Date: Thu, 2 Feb 2023 04:25:52 +0300 Subject: [PATCH 07/19] Fix radial gradient --- src/draw_functions.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/draw_functions.py b/src/draw_functions.py index c1bcf88..8fd8714 100644 --- a/src/draw_functions.py +++ b/src/draw_functions.py @@ -92,10 +92,11 @@ def wave_circle(sample, cr, width, height, colors, radius, thickness, fill): min_radius = min(width, height) * radius / 200 max_radius = min(width, height) / 2 set_source_radial(cr, width / 2, height / 2, min_radius, \ - max_radius + thickness, colors) + max_radius, colors) if not fill: cr.rectangle(0, 0, width, height) - cr.arc_negative(width / 2, height / 2, min_radius - thickness / 2, 2 * math.pi, 0) + cr.arc_negative(width / 2, height / 2, min_radius - thickness / 2, \ + 2 * math.pi, 0) cr.clip() cr.new_path() cr.move_to(width / 2 + min_radius, height / 2) From 4233a5e9ab6c1d27ab2157e7e2b58424e683e411 Mon Sep 17 00:00:00 2001 From: Fyodor Sobolev <117388856+fsobolev@users.noreply.github.com> Date: Thu, 2 Feb 2023 06:12:05 +0300 Subject: [PATCH 08/19] Bars mode Circle variant --- src/draw_functions.py | 38 ++++++++++++++++++++++++++++++++++++++ src/drawing_area.py | 8 ++++++-- 2 files changed, 44 insertions(+), 2 deletions(-) diff --git a/src/draw_functions.py b/src/draw_functions.py index 8fd8714..99bcf2d 100644 --- a/src/draw_functions.py +++ b/src/draw_functions.py @@ -229,3 +229,41 @@ def bars(sample, cr, width, height, colors, offset, fill, thickness): height - height * sample[i] + thickness / 2, \ max(step - offset_px * 2 - thickness, 1), height * sample[i] - thickness) cr.fill() if fill else cr.stroke() + +def bars_circle(sample, cr, width, height, colors, offset, radius): + ls = len(sample) + min_radius = min(width, height) * radius / 200 + max_radius = min(width, height) / 2 + set_source_radial(cr, width / 2, height / 2, min_radius, max_radius, colors) + for i in range(ls): + cr.move_to(width / 2 + math.cos(2 * math.pi / ls * \ + (i + offset / 100) - 0.5 * math.pi) * min_radius, \ + height / 2 + math.sin(2 * math.pi / ls * \ + (i + offset / 100) - 0.5 * math.pi) * min_radius) + cr.line_to(width / 2 + math.cos(2 * math.pi / ls * \ + (i + offset / 100) - 0.5 * math.pi) * \ + (min_radius + sample[i] * (max_radius - min_radius)), \ + height / 2 + math.sin(2 * math.pi / ls * \ + (i + offset / 100) - 0.5 * math.pi) * \ + (min_radius + sample[i] * (max_radius - min_radius))) + cr.line_to(width / 2 + math.cos(2 * math.pi / ls * \ + (i + 1 - offset / 100) - 0.5 * math.pi) * \ + (min_radius + sample[i] * (max_radius - min_radius)), \ + height / 2 + math.sin(2 * math.pi / ls * \ + (i + 1 - offset / 100) - 0.5 * math.pi) * \ + (min_radius + sample[i] * (max_radius - min_radius))) + cr.move_to(width / 2 + math.cos(2 * math.pi / ls * \ + (i + offset / 100) - 0.5 * math.pi) * min_radius, \ + height / 2 + math.sin(2 * math.pi / ls * \ + (i + offset / 100) - 0.5 * math.pi) * min_radius) + cr.line_to(width / 2 + math.cos(2 * math.pi / ls * \ + (i + 1 - offset / 100) - 0.5 * math.pi) * min_radius, \ + height / 2 + math.sin(2 * math.pi / ls * \ + (i + 1 - offset / 100) - 0.5 * math.pi) * min_radius) + cr.line_to(width / 2 + math.cos(2 * math.pi / ls * \ + (i + 1 - offset / 100) - 0.5 * math.pi) * \ + (min_radius + sample[i] * (max_radius - min_radius)), \ + height / 2 + math.sin(2 * math.pi / ls * \ + (i + 1 - offset / 100) - 0.5 * math.pi) * \ + (min_radius + sample[i] * (max_radius - min_radius))) + cr.fill() diff --git a/src/drawing_area.py b/src/drawing_area.py index 2db0c51..dfef253 100644 --- a/src/drawing_area.py +++ b/src/drawing_area.py @@ -118,8 +118,12 @@ def draw_func(self, area, cr, width, height, data, n): spine(self.cava_sample, cr, width, height, self.colors, \ self.offset, self.roundness, self.fill, self.thickness) elif self.draw_mode == 'bars': - bars(self.cava_sample, cr, width, height, self.colors, \ - self.offset, self.fill, self.thickness) + if self.circle: + bars_circle(self.cava_sample, cr, width, height, \ + self.colors, self.offset, self.radius) + else: + bars(self.cava_sample, cr, width, height, self.colors, \ + self.offset, self.fill, self.thickness) def redraw(self): self.queue_draw() From 833e4754ed9aae7e45719cd33545e97e458e9efe Mon Sep 17 00:00:00 2001 From: Fyodor Sobolev <117388856+fsobolev@users.noreply.github.com> Date: Thu, 2 Feb 2023 07:10:06 +0300 Subject: [PATCH 09/19] Bars fill setting --- data/io.github.fsobolev.Cavalier.gschema.xml | 10 +++++++++ src/draw_functions.py | 23 +++++++++++--------- src/drawing_area.py | 4 +++- src/preferences_window.py | 19 ++++++++++++---- 4 files changed, 41 insertions(+), 15 deletions(-) diff --git a/data/io.github.fsobolev.Cavalier.gschema.xml b/data/io.github.fsobolev.Cavalier.gschema.xml index b1a4ab7..2b976d2 100644 --- a/data/io.github.fsobolev.Cavalier.gschema.xml +++ b/data/io.github.fsobolev.Cavalier.gschema.xml @@ -37,6 +37,16 @@ "wave" + + Bars Filling + Filling rule for Bars drawing mode + + + + + + "bars" + Circle Whether to modify drawing modes to draw in circle diff --git a/src/draw_functions.py b/src/draw_functions.py index 99bcf2d..d5370fb 100644 --- a/src/draw_functions.py +++ b/src/draw_functions.py @@ -216,6 +216,7 @@ def spine(sample, cr, width, height, colors, offset, radius, fill, thickness): def bars(sample, cr, width, height, colors, offset, fill, thickness): set_source(cr, height, colors) + cr.set_line_width(thickness) ls = len(sample) step = width / ls offset_px = step * offset / 100 @@ -230,11 +231,13 @@ def bars(sample, cr, width, height, colors, offset, fill, thickness): max(step - offset_px * 2 - thickness, 1), height * sample[i] - thickness) cr.fill() if fill else cr.stroke() -def bars_circle(sample, cr, width, height, colors, offset, radius): +def bars_circle(sample, cr, width, height, colors, offset, fill, thickness, \ + radius): ls = len(sample) min_radius = min(width, height) * radius / 200 max_radius = min(width, height) / 2 set_source_radial(cr, width / 2, height / 2, min_radius, max_radius, colors) + cr.set_line_width(thickness) for i in range(ls): cr.move_to(width / 2 + math.cos(2 * math.pi / ls * \ (i + offset / 100) - 0.5 * math.pi) * min_radius, \ @@ -252,18 +255,18 @@ def bars_circle(sample, cr, width, height, colors, offset, radius): height / 2 + math.sin(2 * math.pi / ls * \ (i + 1 - offset / 100) - 0.5 * math.pi) * \ (min_radius + sample[i] * (max_radius - min_radius))) - cr.move_to(width / 2 + math.cos(2 * math.pi / ls * \ - (i + offset / 100) - 0.5 * math.pi) * min_radius, \ - height / 2 + math.sin(2 * math.pi / ls * \ - (i + offset / 100) - 0.5 * math.pi) * min_radius) - cr.line_to(width / 2 + math.cos(2 * math.pi / ls * \ - (i + 1 - offset / 100) - 0.5 * math.pi) * min_radius, \ - height / 2 + math.sin(2 * math.pi / ls * \ - (i + 1 - offset / 100) - 0.5 * math.pi) * min_radius) cr.line_to(width / 2 + math.cos(2 * math.pi / ls * \ (i + 1 - offset / 100) - 0.5 * math.pi) * \ (min_radius + sample[i] * (max_radius - min_radius)), \ height / 2 + math.sin(2 * math.pi / ls * \ (i + 1 - offset / 100) - 0.5 * math.pi) * \ (min_radius + sample[i] * (max_radius - min_radius))) - cr.fill() + cr.line_to(width / 2 + math.cos(2 * math.pi / ls * \ + (i + 1 - offset / 100) - 0.5 * math.pi) * min_radius, \ + height / 2 + math.sin(2 * math.pi / ls * \ + (i + 1 - offset / 100) - 0.5 * math.pi) * min_radius) + cr.close_path() + if fill == "all": + cr.move_to(width / 2 + min_radius, height / 2) + cr.arc(width / 2, height / 2, min_radius, 0, 2 * math.pi) + cr.stroke() if fill == "outline" else cr.fill() diff --git a/src/drawing_area.py b/src/drawing_area.py index dfef253..266a7b7 100644 --- a/src/drawing_area.py +++ b/src/drawing_area.py @@ -64,6 +64,7 @@ def run(self): def on_settings_changed(self, key): self.draw_mode = self.settings['mode'] + self.bars_fill = self.settings['bars-fill'] self.circle = self.settings['circle'] self.wave_circle_fill = self.settings['wave-circle-fill'] self.radius = self.settings['radius'] @@ -120,7 +121,8 @@ def draw_func(self, area, cr, width, height, data, n): elif self.draw_mode == 'bars': if self.circle: bars_circle(self.cava_sample, cr, width, height, \ - self.colors, self.offset, self.radius) + self.colors, self.offset, self.bars_fill, \ + self.thickness, self.radius) else: bars(self.cava_sample, cr, width, height, self.colors, \ self.offset, self.fill, self.thickness) diff --git a/src/preferences_window.py b/src/preferences_window.py index f6894a4..738c2f5 100644 --- a/src/preferences_window.py +++ b/src/preferences_window.py @@ -48,7 +48,7 @@ def __init__(self, **kwargs): self.create_cava_page() self.create_colors_page() self.settings_bind = False - self.do_not_change_profile = False + self.do_not_update = False self.load_settings() def create_cavalier_page(self): @@ -105,6 +105,10 @@ def create_cavalier_page(self): self.bars_check_btn.set_group(self.wave_check_btn) self.bars_row.add_prefix(self.bars_check_btn) self.bars_row.set_activatable_widget(self.bars_check_btn) + self.bars_fill_dropdown = Gtk.DropDown.new_from_strings( \ + [_('Outline'), _('Fill bars'), _('Fill bars and circle')]) + self.bars_fill_dropdown.set_valign(Gtk.Align.CENTER) + self.bars_row.add_suffix(self.bars_fill_dropdown) self.cavalier_mode_group.add(self.bars_row) self.mode_variant_stack = Gtk.Stack.new() @@ -442,10 +446,14 @@ def create_colors_page(self): self.bg_color_btns = [] def load_settings(self): + self.do_not_update = True (self.wave_check_btn, self.levels_check_btn, \ self.particles_check_btn, self.spine_check_btn, self.bars_check_btn)[ \ self.settings.get_range('mode')[1].index(self.settings['mode']) \ ].set_active(True) + self.bars_fill_dropdown.set_selected( \ + self.settings.get_range('bars-fill')[1].index( \ + self.settings['bars-fill'])) self.mode_variant_stack.set_visible_child_name( \ 'circle' if self.settings['circle'] else 'box') self.wave_circle_fill_box.set_visible(self.settings['circle']) @@ -487,7 +495,6 @@ def load_settings(self): self.bind_settings() profiles = self.settings['color-profiles'] active_profile = self.settings['active-color-profile'] - self.do_not_change_profile = True while self.profiles_list.get_n_items() > 0: self.profiles_list.remove(0) for p in profiles: @@ -498,11 +505,11 @@ def load_settings(self): except: self.settings['active-color-profile'] = 0 return - self.do_not_change_profile = False self.profiles_dropdown.set_selected(active_profile) self.profile_remove_button.set_sensitive(active_profile != 0) self.clear_colors_grid() self.fill_colors_grid() + self.do_not_update = False def bind_settings(self): self.wave_check_btn.connect('toggled', self.change_mode, 'wave') @@ -514,6 +521,10 @@ def bind_settings(self): 'particles') self.spine_check_btn.connect('toggled', self.change_mode, 'spine') self.bars_check_btn.connect('toggled', self.change_mode, 'bars') + self.bars_fill_dropdown.connect('notify::selected-item', \ + lambda *args: self.save_setting(self.bars_fill_dropdown, \ + 'bars-fill', self.settings.get_range('bars-fill')[1][ \ + self.bars_fill_dropdown.get_selected()])) # `notify::visible-child` signal returns additional parameter that # we don't need, that's why lambda is used. self.mode_variant_stack.connect('notify::visible-child', \ @@ -686,7 +697,7 @@ def clear_colors_grid(self): break def select_color_profile(self, obj, pos): - if self.do_not_change_profile: + if self.do_not_update: return if self.profiles_dropdown.get_selected() != \ self.settings['active-color-profile']: From f915ac2769c52c875717fb7bded3b7f506511539 Mon Sep 17 00:00:00 2001 From: Fyodor Sobolev <117388856+fsobolev@users.noreply.github.com> Date: Thu, 2 Feb 2023 07:20:50 +0300 Subject: [PATCH 10/19] Wave Circle - Fix min radius when not filled --- src/draw_functions.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/draw_functions.py b/src/draw_functions.py index d5370fb..3e142ad 100644 --- a/src/draw_functions.py +++ b/src/draw_functions.py @@ -103,6 +103,7 @@ def wave_circle(sample, cr, width, height, colors, radius, thickness, fill): cr.arc(width / 2, height / 2, min_radius, 0, 2 * math.pi) cr.set_line_width(thickness) cr.stroke() + min_radius += thickness cr.move_to( \ width / 2 + math.cos(2 * math.pi / ls * (ls - 0.5) - 0.5 * math.pi) * \ (min_radius + sample[-1] * (max_radius - min_radius)), \ From 0185b2505c9d80735cdedb1f93a0893a99a74b2d Mon Sep 17 00:00:00 2001 From: Fyodor Sobolev <117388856+fsobolev@users.noreply.github.com> Date: Fri, 3 Feb 2023 07:43:27 +0300 Subject: [PATCH 11/19] Rework radius setting --- data/io.github.fsobolev.Cavalier.gschema.xml | 4 ++-- src/draw_functions.py | 4 ++-- src/preferences_window.py | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/data/io.github.fsobolev.Cavalier.gschema.xml b/data/io.github.fsobolev.Cavalier.gschema.xml index 2b976d2..c4a48ab 100644 --- a/data/io.github.fsobolev.Cavalier.gschema.xml +++ b/data/io.github.fsobolev.Cavalier.gschema.xml @@ -60,8 +60,8 @@ Radius Radius of base circle (in percent) - - 20 + + 40 Drawing area margin diff --git a/src/draw_functions.py b/src/draw_functions.py index 3e142ad..1ce4aca 100644 --- a/src/draw_functions.py +++ b/src/draw_functions.py @@ -89,7 +89,7 @@ def wave(sample, cr, width, height, colors, fill, thickness): def wave_circle(sample, cr, width, height, colors, radius, thickness, fill): ls = len(sample) cr.move_to(width / 2, height / 2) - min_radius = min(width, height) * radius / 200 + min_radius = min(width, height) * radius / 400 max_radius = min(width, height) / 2 set_source_radial(cr, width / 2, height / 2, min_radius, \ max_radius, colors) @@ -235,7 +235,7 @@ def bars(sample, cr, width, height, colors, offset, fill, thickness): def bars_circle(sample, cr, width, height, colors, offset, fill, thickness, \ radius): ls = len(sample) - min_radius = min(width, height) * radius / 200 + min_radius = min(width, height) * radius / 400 max_radius = min(width, height) / 2 set_source_radial(cr, width / 2, height / 2, min_radius, max_radius, colors) cr.set_line_width(thickness) diff --git a/src/preferences_window.py b/src/preferences_window.py index 738c2f5..8eb3bff 100644 --- a/src/preferences_window.py +++ b/src/preferences_window.py @@ -127,7 +127,7 @@ def create_cavalier_page(self): self.pref_radius.set_title(_('Radius')) self.pref_radius.set_subtitle(_('Radius of base circle (in percent)')) self.pref_radius_scale = Gtk.Scale.new_with_range( \ - Gtk.Orientation.HORIZONTAL, 0.0, 50.0, 1.0) + Gtk.Orientation.HORIZONTAL, 0.0, 100.0, 1.0) self.pref_radius_scale.set_size_request(180, -1) self.pref_radius_scale.set_draw_value(True) self.pref_radius_scale.set_value_pos(Gtk.PositionType.LEFT) From 9aa8bb9a78f1d2d9c856f343b3bd27a6cbe20691 Mon Sep 17 00:00:00 2001 From: Fyodor Sobolev <117388856+fsobolev@users.noreply.github.com> Date: Fri, 3 Feb 2023 11:54:49 +0300 Subject: [PATCH 12/19] Impove draw_element, particles and spine draw_element now acceps center coordinates instead of top left corner, making it easier to use with circle variants. Also gradients are now better applied in Spine mode --- data/io.github.fsobolev.Cavalier.gschema.xml | 4 +-- src/draw_functions.py | 34 +++++++++++--------- src/preferences_window.py | 6 ++-- 3 files changed, 23 insertions(+), 21 deletions(-) diff --git a/data/io.github.fsobolev.Cavalier.gschema.xml b/data/io.github.fsobolev.Cavalier.gschema.xml index c4a48ab..3d4786d 100644 --- a/data/io.github.fsobolev.Cavalier.gschema.xml +++ b/data/io.github.fsobolev.Cavalier.gschema.xml @@ -78,8 +78,8 @@ Roundness of items This setting affects "levels", "particles" and "spine" modes. - - 10 + + 20 Thickness of lines diff --git a/src/draw_functions.py b/src/draw_functions.py index 1ce4aca..b8f1b0a 100644 --- a/src/draw_functions.py +++ b/src/draw_functions.py @@ -57,16 +57,21 @@ def set_source_radial(cr, x, y, r0, r1, colors): cr.set_source_rgba(red / 255, green / 255, blue / 255, alpha) def draw_element(cr, x, y, width, height, radius): - degrees = math.pi / 180.0 + width /= 2 + height /= 2 cr.new_sub_path() - cr.arc(x + width * radius / 100, y + height * radius / 100, \ - radius * min(width, height) / 100, -180 * degrees, -90 * degrees) - cr.arc(x + width - width * radius / 100, y + height * radius / 100, \ - radius * min(width, height) / 100, -90 * degrees, 0) - cr.arc(x + width - width * radius / 100, y + height - height * radius / 100, \ - radius * min(width, height) / 100, 0, 90 * degrees) - cr.arc(x + width * radius / 100, y + height - height * radius / 100, \ - radius * min(width, height) / 100, 90 * degrees, -180 * degrees) + cr.arc(x + width - width * radius / 100, \ + y + height - height * radius / 100, \ + radius * min(width, height) / 100, 0, 0.5 * math.pi) + cr.arc(x - width + width * radius / 100, \ + y + height - height * radius / 100, \ + radius * min(width, height) / 100, 0.5 * math.pi, math.pi) + cr.arc(x - width + width * radius / 100, \ + y - height + height * radius / 100, \ + radius * min(width, height) / 100, math.pi, 1.5 * math.pi) + cr.arc(x + width - width * radius / 100, \ + y - height + height * radius / 100, \ + radius * min(width, height) / 100, 1.5 * math.pi, 0) cr.close_path() def wave(sample, cr, width, height, colors, fill, thickness): @@ -176,8 +181,8 @@ def particles(sample, cr, width, height, colors, offset, radius, fill, thickness (height / 10) / 2) cr.set_line_width(thickness) for i in range(ls): - draw_element(cr, step * i + offset_px + thickness / 2, \ - height * 0.9 - height * 0.9 * sample[i] + thickness / 2, \ + draw_element(cr, step * (i + 0.5), \ + height * 0.95 - (height * 0.9) * sample[i], \ max(step - offset_px * 2 - thickness, 1), \ max(height / 10 - thickness, 1), radius) cr.fill() if fill else cr.stroke() @@ -196,8 +201,7 @@ def spine(sample, cr, width, height, colors, offset, radius, fill, thickness): thickness = min(thickness, \ (step * sample[i] - offset_px * 2) / 2) cr.set_line_width(thickness) - draw_element(cr, width / 2 - sample[i] * step / 2 + offset_px + thickness / 2, \ - step * i + step / 2 - sample[i] * step / 2 + offset_px + thickness / 2, \ + draw_element(cr, width / 2, step * (i + 0.5), \ step * sample[i] - offset_px * 2 - thickness, \ step * sample[i] - offset_px * 2 - thickness, radius) cr.fill() if fill else cr.stroke() @@ -208,9 +212,7 @@ def spine(sample, cr, width, height, colors, offset, radius, fill, thickness): offset_px = step * offset / 100 * sample[i] if not fill: offset_px += thickness / 2 - draw_element(cr, \ - step * i + step / 2 - sample[i] * step / 2 + offset_px, \ - height / 2 - sample[i] * step / 2 + offset_px, \ + draw_element(cr, step * (i + 0.5), height / 2, step * sample[i] - offset_px * 2, \ step * sample[i] - offset_px * 2, radius) cr.fill() if fill else cr.stroke() diff --git a/src/preferences_window.py b/src/preferences_window.py index 8eb3bff..4af5424 100644 --- a/src/preferences_window.py +++ b/src/preferences_window.py @@ -170,7 +170,7 @@ def create_cavalier_page(self): self.pref_roundness.set_subtitle( \ _('This setting affects "levels", "particles" and "spine" modes.\n0 - square, 1 - round')) self.pref_roundness_scale = Gtk.Scale.new_with_range( \ - Gtk.Orientation.HORIZONTAL, 0.0, 1.0, 0.02) + Gtk.Orientation.HORIZONTAL, 0.0, 1.0, 0.01) self.pref_roundness_scale.set_size_request(190, -1) self.pref_roundness_scale.set_draw_value(True) self.pref_roundness_scale.set_value_pos(Gtk.PositionType.LEFT) @@ -463,7 +463,7 @@ def load_settings(self): self.pref_margin_scale.set_value(self.settings['margin']) self.pref_offset_scale.set_value(self.settings['items-offset']) self.pref_roundness_scale.set_value( \ - round(self.settings['items-roundness'] / 50.0, 2)) + round(self.settings['items-roundness'] / 100.0, 2)) self.pref_thickness_scale.set_value(self.settings['line-thickness']) self.pref_fill_switch.set_active(self.settings['fill']) self.pref_borderless_switch.set_active( \ @@ -538,7 +538,7 @@ def bind_settings(self): 'items-offset', self.pref_offset_scale.get_value) self.pref_roundness_scale.connect('value-changed', self.save_setting, \ 'items-roundness', lambda *args : \ - self.pref_roundness_scale.get_value() * 50.0) + self.pref_roundness_scale.get_value() * 100.0) self.pref_thickness_scale.connect('value-changed', self.save_setting, \ 'line-thickness', self.pref_thickness_scale.get_value) # `notify::state` signal returns additional parameter that From c5755d869071bb1c9767d26c412fe933c2d7a861 Mon Sep 17 00:00:00 2001 From: Fyodor Sobolev <117388856+fsobolev@users.noreply.github.com> Date: Fri, 3 Feb 2023 12:10:29 +0300 Subject: [PATCH 13/19] Fix particles mode It was broken after previous commit --- src/draw_functions.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/draw_functions.py b/src/draw_functions.py index b8f1b0a..9523c78 100644 --- a/src/draw_functions.py +++ b/src/draw_functions.py @@ -162,8 +162,8 @@ def levels(sample, cr, width, height, colors, offset, radius, fill, thickness): for i in range(ls): q = int(round(sample[i], 1) * 10) for r in range(q): - draw_element(cr, step * i + offset_px + thickness / 2, \ - height - (height / 10 * (r + 1)) + offset_px / 2 + thickness / 2, \ + draw_element(cr, step * (i + 0.5), \ + height - (height / 10 * (r + 0.5)), \ max(step - offset_px * 2 - thickness, 1), \ max(height / 10 - offset_px - thickness, 1), radius) cr.fill() if fill else cr.stroke() From 6c93e4a4d8a9e2521ced2b5fb0178b7302042181 Mon Sep 17 00:00:00 2001 From: Fyodor Sobolev <117388856+fsobolev@users.noreply.github.com> Date: Sun, 5 Feb 2023 02:11:41 +0300 Subject: [PATCH 14/19] Remove bars-fill setting and use fill instead --- data/io.github.fsobolev.Cavalier.gschema.xml | 10 ---------- src/draw_functions.py | 5 +---- src/drawing_area.py | 3 +-- src/preferences_window.py | 11 ----------- 4 files changed, 2 insertions(+), 27 deletions(-) diff --git a/data/io.github.fsobolev.Cavalier.gschema.xml b/data/io.github.fsobolev.Cavalier.gschema.xml index 3d4786d..70b79e0 100644 --- a/data/io.github.fsobolev.Cavalier.gschema.xml +++ b/data/io.github.fsobolev.Cavalier.gschema.xml @@ -37,16 +37,6 @@ "wave" - - Bars Filling - Filling rule for Bars drawing mode - - - - - - "bars" - Circle Whether to modify drawing modes to draw in circle diff --git a/src/draw_functions.py b/src/draw_functions.py index 9523c78..9cba529 100644 --- a/src/draw_functions.py +++ b/src/draw_functions.py @@ -269,7 +269,4 @@ def bars_circle(sample, cr, width, height, colors, offset, fill, thickness, \ height / 2 + math.sin(2 * math.pi / ls * \ (i + 1 - offset / 100) - 0.5 * math.pi) * min_radius) cr.close_path() - if fill == "all": - cr.move_to(width / 2 + min_radius, height / 2) - cr.arc(width / 2, height / 2, min_radius, 0, 2 * math.pi) - cr.stroke() if fill == "outline" else cr.fill() + cr.fill() if fill else cr.stroke() diff --git a/src/drawing_area.py b/src/drawing_area.py index 266a7b7..0aee719 100644 --- a/src/drawing_area.py +++ b/src/drawing_area.py @@ -64,7 +64,6 @@ def run(self): def on_settings_changed(self, key): self.draw_mode = self.settings['mode'] - self.bars_fill = self.settings['bars-fill'] self.circle = self.settings['circle'] self.wave_circle_fill = self.settings['wave-circle-fill'] self.radius = self.settings['radius'] @@ -121,7 +120,7 @@ def draw_func(self, area, cr, width, height, data, n): elif self.draw_mode == 'bars': if self.circle: bars_circle(self.cava_sample, cr, width, height, \ - self.colors, self.offset, self.bars_fill, \ + self.colors, self.offset, self.fill, \ self.thickness, self.radius) else: bars(self.cava_sample, cr, width, height, self.colors, \ diff --git a/src/preferences_window.py b/src/preferences_window.py index 4af5424..18e61b2 100644 --- a/src/preferences_window.py +++ b/src/preferences_window.py @@ -105,10 +105,6 @@ def create_cavalier_page(self): self.bars_check_btn.set_group(self.wave_check_btn) self.bars_row.add_prefix(self.bars_check_btn) self.bars_row.set_activatable_widget(self.bars_check_btn) - self.bars_fill_dropdown = Gtk.DropDown.new_from_strings( \ - [_('Outline'), _('Fill bars'), _('Fill bars and circle')]) - self.bars_fill_dropdown.set_valign(Gtk.Align.CENTER) - self.bars_row.add_suffix(self.bars_fill_dropdown) self.cavalier_mode_group.add(self.bars_row) self.mode_variant_stack = Gtk.Stack.new() @@ -451,9 +447,6 @@ def load_settings(self): self.particles_check_btn, self.spine_check_btn, self.bars_check_btn)[ \ self.settings.get_range('mode')[1].index(self.settings['mode']) \ ].set_active(True) - self.bars_fill_dropdown.set_selected( \ - self.settings.get_range('bars-fill')[1].index( \ - self.settings['bars-fill'])) self.mode_variant_stack.set_visible_child_name( \ 'circle' if self.settings['circle'] else 'box') self.wave_circle_fill_box.set_visible(self.settings['circle']) @@ -521,10 +514,6 @@ def bind_settings(self): 'particles') self.spine_check_btn.connect('toggled', self.change_mode, 'spine') self.bars_check_btn.connect('toggled', self.change_mode, 'bars') - self.bars_fill_dropdown.connect('notify::selected-item', \ - lambda *args: self.save_setting(self.bars_fill_dropdown, \ - 'bars-fill', self.settings.get_range('bars-fill')[1][ \ - self.bars_fill_dropdown.get_selected()])) # `notify::visible-child` signal returns additional parameter that # we don't need, that's why lambda is used. self.mode_variant_stack.connect('notify::visible-child', \ From 44ed2b746a149df9b5ac7b5e3e74319218f8e2d5 Mon Sep 17 00:00:00 2001 From: Fyodor Sobolev <117388856+fsobolev@users.noreply.github.com> Date: Sun, 5 Feb 2023 05:46:09 +0300 Subject: [PATCH 15/19] Make Bars Circle mode more or less working --- src/draw_functions.py | 86 +++++++++++++++++++++++++++++-------------- 1 file changed, 59 insertions(+), 27 deletions(-) diff --git a/src/draw_functions.py b/src/draw_functions.py index 9cba529..dfa86d6 100644 --- a/src/draw_functions.py +++ b/src/draw_functions.py @@ -240,33 +240,65 @@ def bars_circle(sample, cr, width, height, colors, offset, fill, thickness, \ min_radius = min(width, height) * radius / 400 max_radius = min(width, height) / 2 set_source_radial(cr, width / 2, height / 2, min_radius, max_radius, colors) - cr.set_line_width(thickness) + if fill: + thickness = 0 + thickness_inner_angle = 0 + else: + thickness = min(thickness, \ + min_radius * math.pi * 2 / ls * 0.25) + cr.set_line_width(thickness) + thickness_inner_angle = math.asin(math.sin(0.25 * math.pi) / \ + math.sqrt(min_radius ** 2 + (thickness / 2) ** 2 - 2 * \ + min_radius * thickness / 2 * math.cos(0.25 * math.pi)) * \ + thickness / 2) for i in range(ls): - cr.move_to(width / 2 + math.cos(2 * math.pi / ls * \ - (i + offset / 100) - 0.5 * math.pi) * min_radius, \ - height / 2 + math.sin(2 * math.pi / ls * \ - (i + offset / 100) - 0.5 * math.pi) * min_radius) - cr.line_to(width / 2 + math.cos(2 * math.pi / ls * \ - (i + offset / 100) - 0.5 * math.pi) * \ - (min_radius + sample[i] * (max_radius - min_radius)), \ - height / 2 + math.sin(2 * math.pi / ls * \ - (i + offset / 100) - 0.5 * math.pi) * \ - (min_radius + sample[i] * (max_radius - min_radius))) - cr.line_to(width / 2 + math.cos(2 * math.pi / ls * \ - (i + 1 - offset / 100) - 0.5 * math.pi) * \ - (min_radius + sample[i] * (max_radius - min_radius)), \ - height / 2 + math.sin(2 * math.pi / ls * \ - (i + 1 - offset / 100) - 0.5 * math.pi) * \ - (min_radius + sample[i] * (max_radius - min_radius))) - cr.line_to(width / 2 + math.cos(2 * math.pi / ls * \ - (i + 1 - offset / 100) - 0.5 * math.pi) * \ - (min_radius + sample[i] * (max_radius - min_radius)), \ - height / 2 + math.sin(2 * math.pi / ls * \ - (i + 1 - offset / 100) - 0.5 * math.pi) * \ - (min_radius + sample[i] * (max_radius - min_radius))) - cr.line_to(width / 2 + math.cos(2 * math.pi / ls * \ - (i + 1 - offset / 100) - 0.5 * math.pi) * min_radius, \ - height / 2 + math.sin(2 * math.pi / ls * \ - (i + 1 - offset / 100) - 0.5 * math.pi) * min_radius) + cr.move_to(width / 2 + math.cos( \ + 2 * math.pi / ls * (i + offset / 100) - 0.5 * math.pi + \ + thickness_inner_angle) * (min_radius + thickness / 2), + height / 2 + math.sin( \ + 2 * math.pi / ls * (i + offset / 100) - 0.5 * math.pi + \ + thickness_inner_angle) * (min_radius + thickness / 2)) + cr.line_to(width / 2 + math.cos( \ + 2 * math.pi / ls * (i + offset / 100) - 0.5 * math.pi + \ + math.asin(math.sin(0.25 * math.pi) / math.sqrt((min_radius + \ + max(sample[i] * (max_radius - min_radius), thickness * 2)) ** 2 + \ + (thickness / 2) ** 2 - 2 * (min_radius + sample[i] * \ + (max_radius - min_radius)) * thickness / 2 * \ + math.cos(0.25 * math.pi)) * thickness / 2)) * \ + (min_radius + max(sample[i] * \ + (max_radius - min_radius), thickness * 2) - thickness / 2), \ + height / 2 + math.sin( \ + 2 * math.pi / ls * (i + offset / 100) - 0.5 * math.pi + \ + math.asin(math.sin(0.25 * math.pi) / math.sqrt((min_radius + \ + max(sample[i] * (max_radius - min_radius), thickness * 2)) ** 2 + \ + (thickness / 2) ** 2 - 2 * (min_radius + sample[i] * \ + (max_radius - min_radius)) * thickness / 2 * \ + math.cos(0.25 * math.pi)) * thickness / 2)) * \ + (min_radius + max(sample[i] * \ + (max_radius - min_radius), thickness * 2) - thickness / 2)) + cr.line_to(width / 2 + math.cos( \ + 2 * math.pi / ls * (i + 1 - offset / 100) - 0.5 * math.pi - \ + math.asin(math.sin(0.25 * math.pi) / math.sqrt((min_radius + \ + max(sample[i] * (max_radius - min_radius), thickness * 2)) ** 2 + \ + (thickness / 2) ** 2 - 2 * (min_radius + max(sample[i] * \ + (max_radius - min_radius), thickness * 2)) * thickness / 2 * \ + math.cos(0.25 * math.pi)) * thickness / 2)) * \ + (min_radius + max(sample[i] * \ + (max_radius - min_radius), thickness * 2) - thickness / 2), \ + height / 2 + math.sin( \ + 2 * math.pi / ls * (i + 1 - offset / 100) - 0.5 * math.pi - \ + math.asin(math.sin(0.25 * math.pi) / math.sqrt((min_radius + \ + max(sample[i] * (max_radius - min_radius), thickness * 2)) ** 2 + \ + (thickness / 2) ** 2 - 2 * (min_radius + max(sample[i] * \ + (max_radius - min_radius), thickness * 2)) * thickness / 2 * \ + math.cos(0.25 * math.pi)) * thickness / 2)) * \ + (min_radius + max(sample[i] * \ + (max_radius - min_radius), thickness * 2) - thickness / 2)) + cr.line_to(width / 2 + math.cos( \ + 2 * math.pi / ls * (i + 1 - offset / 100) - 0.5 * math.pi - \ + thickness_inner_angle) * (min_radius + thickness / 2), \ + height / 2 + math.sin( \ + 2 * math.pi / ls * (i + 1 - offset / 100) - 0.5 * math.pi - \ + thickness_inner_angle) * (min_radius + thickness / 2)) cr.close_path() cr.fill() if fill else cr.stroke() From 4abee931aa95b601a62ebdacc02fcedd2657fe0b Mon Sep 17 00:00:00 2001 From: Fyodor Sobolev <117388856+fsobolev@users.noreply.github.com> Date: Sun, 5 Feb 2023 06:12:50 +0300 Subject: [PATCH 16/19] Improve box/circle preferences --- src/preferences_window.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/preferences_window.py b/src/preferences_window.py index 18e61b2..7a92e83 100644 --- a/src/preferences_window.py +++ b/src/preferences_window.py @@ -113,8 +113,9 @@ def create_cavalier_page(self): self.box_group = Adw.PreferencesGroup.new() self.mode_variant_stack.add_titled(self.box_group, 'box', _('Box')) - self.mirror_row = Adw.ActionRow.new() - self.mirror_row.set_title(_('Mirror')) + self.pref_mirror = Adw.ActionRow.new() + self.pref_mirror.set_title(_('Mirror')) + self.box_group.add(self.pref_mirror) self.circle_group = Adw.PreferencesGroup.new() self.mode_variant_stack.add_titled(self.circle_group, 'circle', \ @@ -449,6 +450,8 @@ def load_settings(self): ].set_active(True) self.mode_variant_stack.set_visible_child_name( \ 'circle' if self.settings['circle'] else 'box') + self.box_group.set_visible(not self.settings['circle']) + self.circle_group.set_visible(self.settings['circle']) self.wave_circle_fill_box.set_visible(self.settings['circle']) self.wave_circle_fill_switch.set_active( \ self.settings['wave-circle-fill']) From e35fe1a9cf22198e18f853f38f92c1f424e24724 Mon Sep 17 00:00:00 2001 From: gregorni <119804311+gregorni@users.noreply.github.com> Date: Sun, 5 Feb 2023 12:30:45 +0100 Subject: [PATCH 17/19] add line circle mode to wave circle mode --- src/draw_functions.py | 7 ++++--- src/drawing_area.py | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/draw_functions.py b/src/draw_functions.py index dfa86d6..da34a09 100644 --- a/src/draw_functions.py +++ b/src/draw_functions.py @@ -91,14 +91,14 @@ def wave(sample, cr, width, height, colors, fill, thickness): cr.set_line_width(thickness) cr.stroke() -def wave_circle(sample, cr, width, height, colors, radius, thickness, fill): +def wave_circle(sample, cr, width, height, colors, radius, fill, thickness, fill_middle): ls = len(sample) cr.move_to(width / 2, height / 2) min_radius = min(width, height) * radius / 400 max_radius = min(width, height) / 2 set_source_radial(cr, width / 2, height / 2, min_radius, \ max_radius, colors) - if not fill: + if not fill_middle: cr.rectangle(0, 0, width, height) cr.arc_negative(width / 2, height / 2, min_radius - thickness / 2, \ 2 * math.pi, 0) @@ -145,7 +145,8 @@ def wave_circle(sample, cr, width, height, colors, radius, thickness, fill): (min_radius + sample[i+1] * (max_radius - min_radius)) ) cr.close_path() # required to avoid artifact with thick lines - cr.fill() + cr.set_line_width(thickness) + cr.fill() if fill else cr.stroke() def levels(sample, cr, width, height, colors, offset, radius, fill, thickness): set_source(cr, height, colors) diff --git a/src/drawing_area.py b/src/drawing_area.py index 0aee719..b0f64dc 100644 --- a/src/drawing_area.py +++ b/src/drawing_area.py @@ -103,7 +103,7 @@ def draw_func(self, area, cr, width, height, data, n): if self.draw_mode == 'wave': if self.circle: wave_circle(self.cava_sample, cr, width, height, \ - self.colors, self.radius, self.thickness, \ + self.colors, self.radius, self.fill, self.thickness, \ self.wave_circle_fill) else: wave(self.cava_sample, cr, width, height, self.colors, \ From bef1f13970c32d1a3f2f008605c90d27fabd4aae Mon Sep 17 00:00:00 2001 From: gregorni <119804311+gregorni@users.noreply.github.com> Date: Sun, 5 Feb 2023 12:52:10 +0100 Subject: [PATCH 18/19] improve & rename Wave mode fill circle setting --- data/io.github.fsobolev.Cavalier.gschema.xml | 4 ++-- src/draw_functions.py | 4 ++-- src/preferences_window.py | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/data/io.github.fsobolev.Cavalier.gschema.xml b/data/io.github.fsobolev.Cavalier.gschema.xml index 70b79e0..439a064 100644 --- a/data/io.github.fsobolev.Cavalier.gschema.xml +++ b/data/io.github.fsobolev.Cavalier.gschema.xml @@ -43,8 +43,8 @@ false - Wave - Fill Circle - Whether to fill the inner circle when using Wave mode Circle variant. + Wave - Show Inner Circle + Whether to show the inner circle when using Wave mode Circle variant. false diff --git a/src/draw_functions.py b/src/draw_functions.py index da34a09..9a42d57 100644 --- a/src/draw_functions.py +++ b/src/draw_functions.py @@ -91,14 +91,14 @@ def wave(sample, cr, width, height, colors, fill, thickness): cr.set_line_width(thickness) cr.stroke() -def wave_circle(sample, cr, width, height, colors, radius, fill, thickness, fill_middle): +def wave_circle(sample, cr, width, height, colors, radius, fill, thickness, show_inner_circle): ls = len(sample) cr.move_to(width / 2, height / 2) min_radius = min(width, height) * radius / 400 max_radius = min(width, height) / 2 set_source_radial(cr, width / 2, height / 2, min_radius, \ max_radius, colors) - if not fill_middle: + if show_inner_circle: cr.rectangle(0, 0, width, height) cr.arc_negative(width / 2, height / 2, min_radius - thickness / 2, \ 2 * math.pi, 0) diff --git a/src/preferences_window.py b/src/preferences_window.py index 7a92e83..9a95968 100644 --- a/src/preferences_window.py +++ b/src/preferences_window.py @@ -68,7 +68,7 @@ def create_cavalier_page(self): self.wave_row.set_activatable_widget(self.wave_check_btn) self.wave_circle_fill_box = Gtk.Box.new(Gtk.Orientation.HORIZONTAL, 10) self.wave_row.add_suffix(self.wave_circle_fill_box) - self.wave_circle_fill_label = Gtk.Label.new(_('Fill completely')) + self.wave_circle_fill_label = Gtk.Label.new(_('Show inner circle')) self.wave_circle_fill_box.append(self.wave_circle_fill_label) self.wave_circle_fill_switch = Gtk.Switch.new() self.wave_circle_fill_switch.set_valign(Gtk.Align.CENTER) From ce18fead1c0f8c76adbb221da1f855e1ead5fc8a Mon Sep 17 00:00:00 2001 From: gregorni <119804311+gregorni@users.noreply.github.com> Date: Sun, 5 Feb 2023 13:18:26 +0100 Subject: [PATCH 19/19] Add shortcut for inner circle & more renaming --- data/io.github.fsobolev.Cavalier.gschema.xml | 2 +- src/draw_functions.py | 4 +-- src/drawing_area.py | 4 +-- src/preferences_window.py | 26 ++++++++++---------- src/shortcuts.py | 7 ++++++ src/shortcuts_dialog.ui | 6 +++++ 6 files changed, 31 insertions(+), 18 deletions(-) diff --git a/data/io.github.fsobolev.Cavalier.gschema.xml b/data/io.github.fsobolev.Cavalier.gschema.xml index 439a064..0330015 100644 --- a/data/io.github.fsobolev.Cavalier.gschema.xml +++ b/data/io.github.fsobolev.Cavalier.gschema.xml @@ -42,7 +42,7 @@ Whether to modify drawing modes to draw in circle false - + Wave - Show Inner Circle Whether to show the inner circle when using Wave mode Circle variant. false diff --git a/src/draw_functions.py b/src/draw_functions.py index 9a42d57..e4cc1e8 100644 --- a/src/draw_functions.py +++ b/src/draw_functions.py @@ -91,14 +91,14 @@ def wave(sample, cr, width, height, colors, fill, thickness): cr.set_line_width(thickness) cr.stroke() -def wave_circle(sample, cr, width, height, colors, radius, fill, thickness, show_inner_circle): +def wave_circle(sample, cr, width, height, colors, radius, fill, thickness, inner_circle): ls = len(sample) cr.move_to(width / 2, height / 2) min_radius = min(width, height) * radius / 400 max_radius = min(width, height) / 2 set_source_radial(cr, width / 2, height / 2, min_radius, \ max_radius, colors) - if show_inner_circle: + if inner_circle: cr.rectangle(0, 0, width, height) cr.arc_negative(width / 2, height / 2, min_radius - thickness / 2, \ 2 * math.pi, 0) diff --git a/src/drawing_area.py b/src/drawing_area.py index b0f64dc..7a80712 100644 --- a/src/drawing_area.py +++ b/src/drawing_area.py @@ -65,7 +65,7 @@ def run(self): def on_settings_changed(self, key): self.draw_mode = self.settings['mode'] self.circle = self.settings['circle'] - self.wave_circle_fill = self.settings['wave-circle-fill'] + self.wave_inner_circle = self.settings['wave-inner-circle'] self.radius = self.settings['radius'] self.set_margin_top(self.settings['margin']) self.set_margin_bottom(self.settings['margin']) @@ -104,7 +104,7 @@ def draw_func(self, area, cr, width, height, data, n): if self.circle: wave_circle(self.cava_sample, cr, width, height, \ self.colors, self.radius, self.fill, self.thickness, \ - self.wave_circle_fill) + self.wave_inner_circle) else: wave(self.cava_sample, cr, width, height, self.colors, \ self.fill, self.thickness) diff --git a/src/preferences_window.py b/src/preferences_window.py index 9a95968..572030c 100644 --- a/src/preferences_window.py +++ b/src/preferences_window.py @@ -66,13 +66,13 @@ def create_cavalier_page(self): self.wave_check_btn = Gtk.CheckButton.new() self.wave_row.add_prefix(self.wave_check_btn) self.wave_row.set_activatable_widget(self.wave_check_btn) - self.wave_circle_fill_box = Gtk.Box.new(Gtk.Orientation.HORIZONTAL, 10) - self.wave_row.add_suffix(self.wave_circle_fill_box) - self.wave_circle_fill_label = Gtk.Label.new(_('Show inner circle')) - self.wave_circle_fill_box.append(self.wave_circle_fill_label) - self.wave_circle_fill_switch = Gtk.Switch.new() - self.wave_circle_fill_switch.set_valign(Gtk.Align.CENTER) - self.wave_circle_fill_box.append(self.wave_circle_fill_switch) + self.wave_inner_circle_box = Gtk.Box.new(Gtk.Orientation.HORIZONTAL, 10) + self.wave_row.add_suffix(self.wave_inner_circle_box) + self.wave_inner_circle_label = Gtk.Label.new(_('Show inner circle')) + self.wave_inner_circle_box.append(self.wave_inner_circle_label) + self.wave_inner_circle_switch = Gtk.Switch.new() + self.wave_inner_circle_switch.set_valign(Gtk.Align.CENTER) + self.wave_inner_circle_box.append(self.wave_inner_circle_switch) self.cavalier_mode_group.add(self.wave_row) self.levels_row = Adw.ActionRow.new() @@ -452,9 +452,9 @@ def load_settings(self): 'circle' if self.settings['circle'] else 'box') self.box_group.set_visible(not self.settings['circle']) self.circle_group.set_visible(self.settings['circle']) - self.wave_circle_fill_box.set_visible(self.settings['circle']) - self.wave_circle_fill_switch.set_active( \ - self.settings['wave-circle-fill']) + self.wave_inner_circle_box.set_visible(self.settings['circle']) + self.wave_inner_circle_switch.set_active( \ + self.settings['wave-inner-circle']) self.pref_radius_scale.set_value(self.settings['radius']) self.pref_margin_scale.set_value(self.settings['margin']) self.pref_offset_scale.set_value(self.settings['items-offset']) @@ -509,9 +509,9 @@ def load_settings(self): def bind_settings(self): self.wave_check_btn.connect('toggled', self.change_mode, 'wave') - self.wave_circle_fill_switch.connect('notify::state', \ - lambda *args : self.save_setting(self.wave_circle_fill_switch, \ - 'wave-circle-fill', self.wave_circle_fill_switch.get_state())) + self.wave_inner_circle_switch.connect('notify::state', \ + lambda *args : self.save_setting(self.wave_inner_circle_switch, \ + 'wave-inner-circle', self.wave_inner_circle_switch.get_state())) self.levels_check_btn.connect('toggled', self.change_mode, 'levels') self.particles_check_btn.connect('toggled', self.change_mode, \ 'particles') diff --git a/src/shortcuts.py b/src/shortcuts.py index 411d8e9..b864890 100644 --- a/src/shortcuts.py +++ b/src/shortcuts.py @@ -110,6 +110,13 @@ def add_shortcuts(widget, settings): Gtk.ShortcutTrigger.parse_string("T"), \ Gtk.NamedAction.new("cavalier.decrease-thickness"))) + act_toggle_inner_circle = Gio.SimpleAction.new("toggle-inner-circle", None) + act_toggle_inner_circle.connect('activate', toggle_setting, settings, 'wave-inner-circle') + action_map.add_action(act_toggle_inner_circle) + shortcut_controller.add_shortcut(Gtk.Shortcut.new( \ + Gtk.ShortcutTrigger.parse_string("I"), \ + Gtk.NamedAction.new("cavalier.toggle-inner-circle"))) + act_toggle_fill = Gio.SimpleAction.new("toggle-fill", None) act_toggle_fill.connect('activate', toggle_setting, settings, 'fill') action_map.add_action(act_toggle_fill) diff --git a/src/shortcuts_dialog.ui b/src/shortcuts_dialog.ui index 68a4771..a6cd627 100644 --- a/src/shortcuts_dialog.ui +++ b/src/shortcuts_dialog.ui @@ -34,6 +34,12 @@ R <Shift>R + + + Toggle Inner Circle + I + + Toggle Filling