def draw_clock_hand(self, canvas): inner_p = (int(self.rng1 * math.cos(math.radians(self.theta)) + self.center[0]), int(-self.rng1 * math.sin(math.radians(self.theta)) + self.center[1])) outer_p = (int(self.rng2 * math.cos(math.radians(self.theta)) + self.center[0]), int(-self.rng2 * math.sin(math.radians(self.theta)) + self.center[1])) cv2.line(canvas, inner_p, outer_p, color_to_byte(PALE_GRAY), thickness=2) # 時計の針の先 # 楕円、描画する画像を指定、座標(x,y),xyの半径、角度,色、線の太さ(-1は塗りつぶし) start_angle = int(self.theta - self.unit_arc / 2) end_angle = int(self.theta + self.unit_arc / 2) if start_angle == end_angle: end_angle += 1 # 差が 0 だと変なとこ描画するんで cv2.ellipse(canvas, self.center, (self.rng2, self.rng2), 0, 360 - start_angle, 360 - end_angle, color_to_byte(PALE_GRAY), thickness=self.tickness) cv2.ellipse(canvas, self.center, (self.rng3, self.rng3), 0, 360 - start_angle, 360 - end_angle, color_to_byte(PALE_GRAY), thickness=self.tickness)
def draw_border(self, canvas): """背景の左限、右限の線""" diameter = 2 * self.range1 half_height = diameter * math.tan(math.radians(30)) # 角30°の補助線(外接する矩形の縦幅の1/4) #left = int(self.center[0]) #top = int(self.center[1]-self.range1 * math.tan(math.radians(30))) #right = int(self.center[0]+self.range1) #bottom = int(self.center[1]) # cv2.line(canvas, # (right, top), # (left, bottom), # color_to_byte(BLUE), # thickness=1) # 角60°の補助線(定義から、外接する矩形の左上の角) #left = int(self.center[0]-self.range1) #top = int(self.center[1]-diameter * math.tan(math.radians(30))) #right = int(self.center[0]) #bottom = int(self.center[1]) # cv2.line(canvas, # (left, top), # (right, bottom), # color_to_byte(RED), # thickness=2) # 角270°の補助線(南側の垂線) #left = int(self.center[0]) #top = int(self.center[1]) #right = int(self.center[0]) #bottom = int(self.center[1] + diameter * math.tan(math.radians(30))) # cv2.line(canvas, # (left, top), # (right, bottom), # color_to_byte(PALE_GRAY), # thickness=1) # 矩形 left = int(self.center[0] - self.range1) top = int(self.center[1] - half_height) right = int(self.center[0] + self.range1) bottom = int(self.center[1] + half_height) cv2.rectangle(canvas, (left, top), (right, bottom), color_to_byte(PALE_GRAY), thickness=2) # 左限の線 cv2.line(canvas, (int(self.center[0] - self.range1), self.__drawing_top), (int(self.center[0] - self.range1), self.__drawing_bottom), color_to_byte(PALE_GRAY), thickness=2) # 右限の線 cv2.line(canvas, (int(self.center[0] + self.range1), self.__drawing_top), (int(self.center[0] + self.range1), self.__drawing_bottom), color_to_byte(PALE_GRAY), thickness=2)
def draw_border(self, canvas): """背景の左限、右限の線""" diameter = 2 * self.range1 half_height = diameter * math.tan(math.radians(30)) # 矩形 left = int(self.center[0] - self.range1) top = int(self.center[1] - half_height) right = int(self.center[0] + self.range1) bottom = int(self.center[1] + half_height) cv2.rectangle(canvas, (left, top), (right, bottom), color_to_byte(PALE_GRAY), thickness=2) # 左限の線 cv2.line(canvas, (int(self.center[0] - self.range1), self.__drawing_top), (int(self.center[0] - self.range1), self.__drawing_bottom), color_to_byte(PALE_GRAY), thickness=2) # 右限の線 cv2.line(canvas, (int(self.center[0] + self.range1), self.__drawing_top), (int(self.center[0] + self.range1), self.__drawing_bottom), color_to_byte(PALE_GRAY), thickness=2)
def draw_canvas(canvas, bar_box, circle_rail, outer_circle, inscribed_triangle, clock_hand): """アニメの1コマを作成します""" circle_rail.draw_circle(canvas) # 円レール circle_rail.draw_triangle(canvas) # 円に内接する正三角形 circle_rail.draw_border(canvas) # 背景の上限、下限の線 inscribed_triangle.draw(canvas) bar_box.draw_3bars(canvas) # RGBバー bar_box.draw_x_axis_label(canvas) # X軸のラベル # 水平線R # 線、描画する画像を指定、座標1点目、2点目、色、線の太さ cv2.line(canvas, inscribed_triangle.rbg_points[0], (bar_box.red_right, bar_box.red_top), color_to_byte(RED), thickness=2) # 水平線G cv2.line( canvas, inscribed_triangle.rbg_points[2], # 青と緑が入れ替わっているのが工夫 (bar_box.green_right, bar_box.green_top), color_to_byte(GREEN), thickness=2) # 水平線B cv2.line(canvas, inscribed_triangle.rbg_points[1], (bar_box.blue_right, bar_box.blue_top), color_to_byte(BLUE), thickness=2) outer_circle.draw_me(canvas) # 外環状 # 時計の針 clock_hand.draw_clock_hand(canvas) # バー箱の2段目の黒枠 bar_box.draw_rank2_box(canvas) # 色成分数 # 1色成分 n3bars_width = bar_box.create_3bars_width() color = convert_3bars_to_3bytes(n3bars_width, bar_box.width) bar_box.draw_rgb_number(canvas, color) return canvas
def draw_me(self, canvas): """描きます""" # 色相環 color_count = len(self.color_list) # print( # f"color_count={color_count} self.area_size=({self.area_size[0]}, {self.area_size[1]})") for i in range(0, color_count): theta = i * self.unit_arc color = self.color_list[i] # print(f"[{i}] theta={theta} color={color}") # 円弧 # 楕円、描画する画像を指定、座標(x,y),xyの半径、角度,色、線の太さ(-1は塗りつぶし) start_angle = int(theta - self.unit_arc / 2) end_angle = int(theta + self.unit_arc / 2) if start_angle == end_angle: end_angle += 1 # 差が 0 だと変なとこ描画するんで cv2.ellipse(canvas, self.origin, self.area_size, 0, 360 - start_angle, 360 - end_angle, color_to_byte(color), thickness=self.tickness)
def draw_blue_p(self, canvas): """円周上の点Bを描きます""" cv2.circle(canvas, self.blue_p, self.point_range, color_to_byte(BLUE), thickness=-1)
def draw_green_p(self, canvas): """円周上の点Gを描きます""" cv2.circle(canvas, self.green_p, self.point_range, color_to_byte(GREEN), thickness=-1)
def draw_red_p(self, canvas): """円周上の点Rを描きます""" cv2.circle(canvas, self.red_p, self.point_range, color_to_byte(RED), thickness=-1)
def draw_triangle(self, canvas): """円に内接する線。正三角形""" cv2.line(canvas, self.red_p, self.green_p, color_to_byte(WHITE), thickness=2) cv2.line(canvas, self.green_p, self.blue_p, color_to_byte(WHITE), thickness=2) cv2.line(canvas, self.blue_p, self.red_p, color_to_byte(WHITE), thickness=2)
def draw_circle(self, canvas): """描きます""" # 円レール。描画する画像を指定、座標(x,y),半径、色、線の太さ(-1は塗りつぶし) cv2.circle(canvas, self.center, self.range1, color_to_byte(WHITE), thickness=2)
def draw_tone_name(canvas, bar_box, tone_name): """トーン名を描きます""" line_type = 2 cv2.putText( canvas, f"{tone_name}", (bar_box.left, int(BAR_BOX_TOP - 3.5 * GRID_UNIT)), # x,y cv2.FONT_HERSHEY_SIMPLEX, FONT_SCALE, color_to_byte(DARK_GRAYISH_GRAY), line_type) cv2.putText( canvas, f"tone diameter", (bar_box.left + GRID_UNIT, int(BAR_BOX_TOP - 2.5 * GRID_UNIT)), # x,y cv2.FONT_HERSHEY_SIMPLEX, FONT_SCALE, color_to_byte(DARK_GRAYISH_GRAY), line_type)
def draw(self, canvas): """描きます""" cv2.line(canvas, self.rbg_points[0], self.rbg_points[1], color_to_byte(BLACK), thickness=2) cv2.line(canvas, self.rbg_points[1], self.rbg_points[2], color_to_byte(BLACK), thickness=2) cv2.line(canvas, self.rbg_points[2], self.rbg_points[0], color_to_byte(BLACK), thickness=2) cv2.circle(canvas, self.rbg_points[0], int(GRID_UNIT / 4), color_to_byte(RED), thickness=-1) cv2.circle(canvas, self.rbg_points[1], int(GRID_UNIT / 4), color_to_byte(BLUE), thickness=-1) cv2.circle(canvas, self.rbg_points[2], int(GRID_UNIT / 4), color_to_byte(GREEN), thickness=-1)
def draw_canvas(canvas, bar_box, circle_rail, outer_circle, inscribed_triangle, clock_hand): """アニメの1コマを作成します""" circle_rail.draw_circle(canvas) # 円レール circle_rail.draw_triangle(canvas) # 円に内接する正三角形 circle_rail.draw_border(canvas) # 背景の上限、下限の線 inscribed_triangle.draw(canvas) bar_box.draw_3bars(canvas) # RGBバー bar_box.draw_x_axis_label(canvas) # X軸のラベル # 水平線R # 線、描画する画像を指定、座標1点目、2点目、色、線の太さ cv2.line(canvas, inscribed_triangle.rbg_points[0], (bar_box.red_right, bar_box.red_top), color_to_byte(RED), thickness=2) # 水平線G cv2.line( canvas, inscribed_triangle.rbg_points[2], # 青と緑が入れ替わっているのが工夫 (bar_box.green_right, bar_box.green_top), color_to_byte(GREEN), thickness=2) # 水平線B cv2.line(canvas, inscribed_triangle.rbg_points[1], (bar_box.blue_right, bar_box.blue_top), color_to_byte(BLUE), thickness=2) outer_circle.draw_me(canvas) # 外環状 # 時計の針 clock_hand.draw_clock_hand(canvas) # バー箱の2段目の黒枠 bar_box.draw_rank2_box(canvas) # 色成分数 # 1色成分 n3bars_width = bar_box.create_3bars_width() color = convert_3bars_to_3bytes(n3bars_width, bar_box.width) bar_box.draw_rgb_number(canvas, color) # gravity = inscribed_triangle.triangular_center_of_gravity() # debug # cv2.putText(canvas, # f"theta={circle_rail.theta}", # # f"theta={circle_rail.theta} phase={outer_circle.phase} \ # gravity=({gravity[0]:5.1f}, {gravity[1]:5.1f})", # (10, 10), # x,y # cv2.FONT_HERSHEY_SIMPLEX, # FONT_SCALE, # color_to_byte(BLACK), # lineType=2) # f"multiple=({n3bars_multiple[0]:7.3f}, {n3bars_multiple[1]:7.3f}, {n3bars_multiple[2]:7.3f})", # cv2.imshow('Title', canvas) # cv2.imwrite('form.jpg',canvas) # cv2.waitKey(0) # cv2.destroyAllWindows() return canvas
def make_canvas(): """キャンバス生成""" return np.full((CANVAS_HEIGHT, CANVAS_WIDTH, CHANNELS), color_to_byte(SOFT_GRAY)[0], dtype=np.uint8)
def draw_canvas(canvas, bar_box, circle_rail, outer_circle, inscribed_triangle, clock_hand): """アニメの1コマを作成します""" circle_rail.draw_circle(canvas) # 円レール circle_rail.draw_triangle(canvas) # 円に内接する正三角形 circle_rail.draw_border(canvas) # 背景の上限、下限の線 inscribed_triangle.draw(canvas) bar_box.draw_3bars(canvas) # RGBバー bar_box.draw_x_axis_label(canvas) # X軸のラベル # 水平線R # 線、描画する画像を指定、座標1点目、2点目、色、線の太さ cv2.line(canvas, inscribed_triangle.rbg_points[0], (bar_box.red_right, bar_box.red_top), color_to_byte(RED), thickness=2) # 水平線G cv2.line( canvas, inscribed_triangle.rbg_points[2], # 青と緑が入れ替わっているのが工夫 (bar_box.green_right, bar_box.green_top), color_to_byte(GREEN), thickness=2) # 水平線B cv2.line(canvas, inscribed_triangle.rbg_points[1], (bar_box.blue_right, bar_box.blue_top), color_to_byte(BLUE), thickness=2) outer_circle.draw_me(canvas) # 外環状 # 時計の針 clock_hand.draw_clock_hand(canvas) # バー箱の2段目の黒枠 bar_box.draw_rank2_box(canvas) # 色成分数 # 1色成分 n3bars_width = bar_box.create_3bars_width() color = convert_3bars_to_3bytes(n3bars_width, bar_box.width) bar_box.draw_rgb_number(canvas, color) # テスト red = n3bars_width[0] green = n3bars_width[1] blue = n3bars_width[2] upper = max(red, green, blue) lower = min(red, green, blue) bar_width = red + green + blue - upper - lower - lower diameter = upper - lower radius = diameter / 2 tanjent = diameter - bar_width - radius opposite = (math.sqrt(3) / 2) * tanjent adjacent = radius hipotenuse = math.sqrt(adjacent**2 + opposite**2) ex_radius = 2 * math.tan(math.radians(30)) * radius ex_adjacent = ex_radius ex_hipotenuse = math.sqrt(ex_adjacent**2 + tanjent**2) # テスト タンジェント left = int(circle_rail.center[0] - tanjent) top = int(circle_rail.center[1] - ex_radius) right = int(circle_rail.center[0]) bottom = int(circle_rail.center[1] - ex_radius) cv2.line(canvas, (left, top), (right, bottom), color_to_byte(GREEN), thickness=4) # テスト対辺 left = int(circle_rail.center[0] - opposite) top = int(circle_rail.center[1] - circle_rail.range1) right = int(circle_rail.center[0]) bottom = int(circle_rail.center[1] - circle_rail.range1) cv2.line(canvas, (left, top), (right, bottom), color_to_byte(LIGHT_GREEN), thickness=4) # テスト底辺 cv2.line(canvas, (circle_rail.center[0], int(circle_rail.center[1] - ex_adjacent)), (circle_rail.center[0], circle_rail.center[1]), color_to_byte(RED), thickness=4) # テスト底辺 cv2.line(canvas, (circle_rail.center[0], int(circle_rail.center[1] - adjacent)), (circle_rail.center[0], circle_rail.center[1]), color_to_byte(LIGHT_RED), thickness=4) # atan だとずれる。 asin だとピッタリに見える(^~^)acosは向きが違う(^~^) #test_theta = math.degrees(math.acos(opposite / hipotenuse)) test_theta = math.degrees(math.asin(opposite / hipotenuse)) #test_theta = math.degrees(math.atan(opposite / hipotenuse)) # テスト角度 # cv2.ellipse(canvas, # circle_rail.center, # (2*circle_rail.range1, 2*circle_rail.range1), # 0, # 0, # test_theta, # color_to_byte(RED), # thickness=4) # テスト斜辺 point_x = ex_hipotenuse * \ math.cos(math.radians(test_theta+90)) + circle_rail.center[0] point_y = ex_hipotenuse * \ -math.sin(math.radians(test_theta+90)) + circle_rail.center[1] # print(f"point_x={point_x} point_y={point_y}") cv2.line(canvas, (circle_rail.center[0], circle_rail.center[1]), (int(point_x), int(point_y)), color_to_byte(BLUE), thickness=4) # テスト斜辺 point_x = hipotenuse * \ math.cos(math.radians(test_theta+90)) + circle_rail.center[0] point_y = hipotenuse * \ -math.sin(math.radians(test_theta+90)) + circle_rail.center[1] # print(f"point_x={point_x} point_y={point_y}") cv2.line(canvas, (circle_rail.center[0], circle_rail.center[1]), (int(point_x), int(point_y)), color_to_byte(LIGHT_BLUE), thickness=4) # 角60°の補助線(定義から、外接する矩形の左上の角) #left = int(circle_rail.center[0]-circle_rail.range1) #top = int(circle_rail.center[1]-diameter * math.tan(math.radians(30))) #right = int(circle_rail.center[0]) #bottom = int(circle_rail.center[1]) # cv2.line(canvas, # (left, top), # (right, bottom), # color_to_byte(RED), # thickness=2) # gravity = inscribed_triangle.triangular_center_of_gravity() # debug # cv2.putText(canvas, # f"theta={circle_rail.theta}", # # f"theta={circle_rail.theta} phase={outer_circle.phase} \ # gravity=({gravity[0]:5.1f}, {gravity[1]:5.1f})", # (10, 10), # x,y # cv2.FONT_HERSHEY_SIMPLEX, # FONT_SCALE, # color_to_byte(BLACK), # lineType=2) # f"multiple=({n3bars_multiple[0]:7.3f}, {n3bars_multiple[1]:7.3f}, {n3bars_multiple[2]:7.3f})", # cv2.imshow('Title', canvas) # cv2.imwrite('form.jpg',canvas) # cv2.waitKey(0) # cv2.destroyAllWindows() return canvas