A Pythonista script that displays a polygon and its internal angle.
Swiping left and right causes the number of sides to increase or decrease.
| from scene import * | |
| from math import pi, sin, cos | |
| from datetime import datetime | |
| class PolygonAngleVisualiser(Scene): | |
| def setup(self): | |
| self.touch_start = Point(0, 0) | |
| self.min_side_count = 3 | |
| self.max_side_count = 36 | |
| self.side_count = 4 | |
| self.res_scale = 1 | |
| self.fade_instruction_time = None | |
| # arbitrary scaling to handle the difference between iPad & iPhone resolutions & ratio | |
| self.res_scale = (max(self.size.w, self.size.h) / 812) | |
| def touch_began(self, touch): | |
| self.touch_start = touch.location | |
| def touch_moved(self, touch): | |
| if not self.fade_instruction_time: | |
| self.fade_instruction_time = datetime.now() | |
| touch_distance = touch.location - self.touch_start | |
| movement_threshold = self.size.w / 12 | |
| new_side_count = self.side_count | |
| if abs(touch_distance.x) > movement_threshold: | |
| swipe_direction = -int(touch_distance.x / abs(touch_distance.x)) | |
| new_side_count = new_side_count + swipe_direction | |
| if self.min_side_count <= new_side_count <= self.max_side_count and new_side_count != self.side_count: | |
| self.side_count = new_side_count | |
| self.touch_start = touch.location | |
| def draw(self): | |
| background(0.7, 0.7, 0.7) | |
| external_angle = 2 * pi / self.side_count | |
| internal_angle = pi - external_angle | |
| radius = 100 * self.res_scale | |
| angle_length = radius | |
| padding = 20 * self.res_scale | |
| left_margin = self.size.w / 2 - radius - radius - padding | |
| if left_margin < 20: | |
| left_margin = 20 | |
| top_margin = self.size.h / 2 - radius - radius - 2 * padding | |
| if top_margin < 20: | |
| top_margin = 20 | |
| stroke(0.4, 0.4, 1, 1) | |
| stroke_weight(4) | |
| angle_start = Point(left_margin, self.size.h - top_margin - angle_length) | |
| polygon_start = self.size / 2 | |
| if self.size.h > self.size.w: | |
| polygon_start.y -= 75 * self.res_scale | |
| else: | |
| polygon_start.y -= 30 * self.res_scale | |
| # angle | |
| line( | |
| *(angle_start + Point(0, angle_length)), | |
| *(angle_start)) | |
| line( | |
| *(angle_start), | |
| *(angle_start + Point( | |
| angle_length * sin(internal_angle), | |
| angle_length * cos(internal_angle)))) | |
| # angle line | |
| if self.side_count == 4: | |
| line( | |
| *(angle_start + Point(0, angle_length / 2)), | |
| *(angle_start + Point(angle_length / 2, angle_length / 2))) | |
| line( | |
| *(angle_start + Point(angle_length / 2, angle_length / 2)), | |
| *(angle_start + Point(angle_length / 2, 0))) | |
| else: | |
| sub_angle_divisions = 16 | |
| sub_angle = internal_angle / sub_angle_divisions | |
| for i in range(0, sub_angle_divisions): | |
| line( | |
| *(angle_start + Point( | |
| angle_length / 2 * sin(sub_angle * i), | |
| angle_length / 2 * cos(sub_angle * i))), | |
| *(angle_start + Point( | |
| angle_length / 2 * sin(sub_angle * (i + 1)), | |
| angle_length / 2 * cos(sub_angle * (i + 1))))) | |
| # angle text | |
| angle_in_degrees = round((internal_angle / (2 * pi)) * 360) | |
| text( | |
| "{:0.0f}°".format(angle_in_degrees), | |
| 'Futura', | |
| 20 * self.res_scale, | |
| *(angle_start + Point(1.5 * padding, angle_length / 2 + padding))) | |
| # polygon | |
| for i in range(0, self.side_count): | |
| start_angle = external_angle * i | |
| end_angle = external_angle * (i + 1) | |
| # rotate the square to align with axes | |
| if self.side_count == 4: | |
| start_angle = external_angle * i + external_angle / 2 | |
| end_angle = external_angle * (i + 1) + external_angle / 2 | |
| line_start = polygon_start + radius * Point(sin(start_angle), cos(start_angle)) | |
| line_end = polygon_start + radius * Point(sin(end_angle), cos(end_angle)) | |
| line(*line_start, *line_end) | |
| # side count text | |
| text(str(self.side_count), 'Futura', 40 * self.res_scale, *polygon_start) | |
| # instructions | |
| fade_out_time = 1500 | |
| text_alpha = 1 | |
| if self.fade_instruction_time: | |
| elapsed_time = (datetime.now().timestamp() - self.fade_instruction_time.timestamp()) * 1000 | |
| if elapsed_time < fade_out_time: | |
| text_alpha = 1 - (elapsed_time / fade_out_time) | |
| else: | |
| text_alpha = 0 | |
| tint(1, 1, 1, text_alpha) | |
| if polygon_start.y - radius * 1.2 < radius: | |
| text("Swipe to change shape", 'Futura', 2 * padding, *(polygon_start - Point(0, radius * 1.2))) | |
| else: | |
| text("Swipe to", 'Futura', 2 * padding, *(polygon_start - Point(0, radius * 1.2))) | |
| text("change shape", 'Futura', 2 * padding, *(polygon_start - Point(0, radius * 1.2) - Point(0, 2.5 * padding))) | |
| run(PolygonAngleVisualiser()) |