From f55a9e67721d37fdf83e32556a8a66b0c8516ac5 Mon Sep 17 00:00:00 2001 From: Wolfgang Fahl Date: Mon, 5 Feb 2024 17:27:08 +0100 Subject: [PATCH] refactors radar_chart to separate module --- dcm/__init__.py | 2 +- dcm/dcm_chart.py | 23 ---- dcm/radar_chart.py | 102 ++++++++++++++++++ ...{test_dcm_chart.py => test_radar_chart.py} | 44 ++------ 4 files changed, 110 insertions(+), 61 deletions(-) create mode 100644 dcm/radar_chart.py rename tests/{test_dcm_chart.py => test_radar_chart.py} (56%) diff --git a/dcm/__init__.py b/dcm/__init__.py index f3b4574..970659c 100644 --- a/dcm/__init__.py +++ b/dcm/__init__.py @@ -1 +1 @@ -__version__ = "0.1.15" +__version__ = "0.1.16" diff --git a/dcm/dcm_chart.py b/dcm/dcm_chart.py index 501dc95..107edd0 100644 --- a/dcm/dcm_chart.py +++ b/dcm/dcm_chart.py @@ -4,7 +4,6 @@ @author: wf """ import copy -import math from typing import List, Optional, Tuple from dcm.dcm_core import ( @@ -17,7 +16,6 @@ ) from dcm.svg import SVG, DonutSegment, SVGConfig, SVGNodeConfig - class DcmChart: """ a Dynamic competence map chart @@ -126,27 +124,6 @@ def get_element_config(self, element: CompetenceElement) -> SVGNodeConfig: ) return element_config - def calculate_radar_chart_points( - self, - scores: List[float], - max_score: float, - center: Tuple[float, float], - radius: float, - ) -> List[Tuple[float, float]]: - num_axes = len(scores) - angle_per_axis = 2 * math.pi / num_axes # Angle between each axis in radians - - points = [] - for i, score in enumerate(scores): - angle = angle_per_axis * i # Angle for this axis - # Calculate the distance from the center for this point - distance = (score / max_score) * radius - x = center[0] + distance * math.cos(angle) - y = center[1] + distance * math.sin(angle) - points.append((x, y)) - - return points - def get_stacked_segment( self, level: int, diff --git a/dcm/radar_chart.py b/dcm/radar_chart.py new file mode 100644 index 0000000..9a64391 --- /dev/null +++ b/dcm/radar_chart.py @@ -0,0 +1,102 @@ +''' +Created on 2024-02-05 + +@author: wf +''' +import math +from typing import List, Optional, Tuple +from dcm.svg import SVG, SVGNodeConfig, Polygon + + +class RadarChart: + """ + a radar chart + """ + + def __init__(self,svg:SVG,max_score:float=100.0): + self.svg=svg + self.radius = self.svg.config.width / 2 + self.center_x = self.radius + self.center_y = self.radius + self.max_score=max_score + + def add_scale_circles(self, + num_circles: int = 10, + stroke_width: float = 1.0, + stroke_color: str = "black"): + """ + Add concentric circles to the SVG based on the SVG's configuration. + + Args: + num_circles (int): The number of concentric circles to draw. + stroke_width (float): The stroke width of the circle lines. + stroke_color (str): The color of the circle lines. + """ + for i in range(1, num_circles + 1): + circle_radius = (self.radius * i) / num_circles + self.svg.add_circle( + SVGNodeConfig( + x=self.center_x, + y=self.center_y, + width=circle_radius, + stroke_width=stroke_width, + color=stroke_color, + fill="none" # Ensure circles are not filled + ) + ) + + def calculate_radar_chart_points(self, + scores: List[float]) -> List[Tuple[float, float]]: + """ + Calculate the points for the radar chart based on the given scores. + + Args: + scores (List[float]): The scores to be represented on the radar chart. + + Returns: + List[Tuple[float, float]]: The list of points for the radar chart. + """ + num_axes = len(scores) + angle_per_axis = 2 * math.pi / num_axes # Angle between each axis in radians + + points = [] + for i, score in enumerate(scores): + angle = angle_per_axis * i # Angle for this axis + # Calculate the distance from the center for this point + distance = (score / self.max_score) * self.radius + x = self.center_x + distance * math.cos(angle) + y = self.center_y + distance * math.sin(angle) + points.append((x, y)) + + return points + + def add_scores(self, + scores: List[float], + config: Optional[SVGNodeConfig] = None) -> None: + """ + Add the scores to the radar chart as a polygon. + + Args: + scores (List[float]): The scores to be represented on the radar chart. + config (SVGNodeConfig, optional): The configuration for the polygon representing the scores. + """ # Use the function to calculate points for the scores + radar_points = self.calculate_radar_chart_points( + scores + ) + if config is None: + config=SVGNodeConfig( + color="blue", + fill="none", + stroke_width=2.0, + opacity=0.5 + ) + + # Create a Polygon for the radar chart + radar_chart_polygon = Polygon( + points=radar_points, + fill=config.fill, + stroke_width=config.stroke_width, + color=config.color, + opacity=config.opacity, + ) + self.svg.add_polygon(radar_chart_polygon) diff --git a/tests/test_dcm_chart.py b/tests/test_radar_chart.py similarity index 56% rename from tests/test_dcm_chart.py rename to tests/test_radar_chart.py index 46b3fe0..f408c05 100644 --- a/tests/test_dcm_chart.py +++ b/tests/test_radar_chart.py @@ -7,7 +7,7 @@ from ngwidgets.basetest import Basetest -from dcm.dcm_chart import DcmChart +from dcm.radar_chart import RadarChart from dcm.svg import SVG, Polygon, SVGConfig, SVGNodeConfig @@ -60,43 +60,13 @@ def test_radar_chart(self): """ test radar chart creation """ - dcm_chart = DcmChart(None) - # Define the center and radius for the radar chart - center = (300, 300) # Adjust as needed - radius = 200 # Adjust as needed - - # Use the function to calculate points for the scores - radar_points = dcm_chart.calculate_radar_chart_points( - self.scores, max_score=100.0, center=center, radius=radius - ) - - # Create a Polygon for the radar chart - radar_chart_polygon = Polygon( - points=radar_points, - fill="none", - stroke_width=2.0, - color="blue", - opacity=0.5, - ) - - # Create an SVG instance and add the radar chart Polygon + # Create an SVG instance svg = SVG(SVGConfig(width=600, height=600)) - # Add concentric circles at every 10% interval - for i in range(1, 11): - circle_radius = (radius * i) / 10 - svg.add_circle( - SVGNodeConfig( - x=center[0], - y=center[1], - width=circle_radius, - fill="none", - stroke_width=1.0, - color="black", - ) - ) - svg.add_polygon(radar_chart_polygon) - + radar_chart = RadarChart(svg,max_score=100.0) + radar_chart.add_scale_circles() + radar_chart.add_scores(self.scores) + # Save the SVG to a file or inspect the SVG markup svg_file_name = "radar_chart.svg" self.save(svg, svg_file_name) @@ -106,4 +76,4 @@ def test_radar_chart(self): svg_markup = svg.get_svg_markup() print(svg_markup) - self.assertTrue("""