diff --git a/constants.py b/constants.py index 3e82f89b..3fda24c5 100644 --- a/constants.py +++ b/constants.py @@ -1,10 +1,8 @@ import os import numpy as np -# DEFAULT_HEIGHT = 1080 -# DEFAULT_WIDTH = 1920 -DEFAULT_HEIGHT = 4*1920 -DEFAULT_WIDTH = 4*1080 +DEFAULT_HEIGHT = 1080 +DEFAULT_WIDTH = 1920 LOW_QUALITY_FRAME_DURATION = 1./20 MEDIUM_QUALITY_FRAME_DURATION = 1./30 diff --git a/mobject/svg_mobject.py b/mobject/svg_mobject.py index 93166522..f349b4f8 100644 --- a/mobject/svg_mobject.py +++ b/mobject/svg_mobject.py @@ -74,7 +74,8 @@ class SVGMobject(VMobject): elif element.tagName in ['polygon', 'polyline']: result.append(self.polygon_to_mobject(element)) else: - warnings.warn("Unknown element type: " + element.tagName) + pass ##TODO + # warnings.warn("Unknown element type: " + element.tagName) result = filter(lambda m : m is not None, result) self.handle_transforms(element, VMobject(*result)) return result diff --git a/topics/objects.py b/topics/objects.py index 8956bfa4..522ace18 100644 --- a/topics/objects.py +++ b/topics/objects.py @@ -11,6 +11,220 @@ from animation.simple_animations import Rotating from topics.geometry import Circle, Line, Rectangle, Square, Arc, Polygon from topics.three_dimensions import Cube +class DeckOfCards(VGroup): + def __init__(self, **kwargs): + possible_values = map(str, range(1, 11)) + ["J", "Q", "K"] + possible_suits = ["hearts", "diamonds", "spades", "clubs"] + VGroup.__init__(self, *[ + PlayingCard(value = value, suit = suit, **kwargs) + for value in possible_values + for suit in possible_suits + ]) + + +class PlayingCard(VGroup): + CONFIG = { + "value" : None, + "suit" : None, + "height" : 1.5, + "height_to_width" : 3.5/2.5, + "card_height_to_symbol_height" : 7, + "card_width_to_corner_num_width" : 10, + "card_height_to_corner_num_height" : 10, + "color" : LIGHT_GREY, + } + def generate_points(self): + self.add(Rectangle( + height = self.height, + width = self.height/self.height_to_width, + stroke_color = WHITE, + stroke_width = 2, + fill_color = self.color, + fill_opacity = 1, + )) + value = self.get_value() + symbol = self.get_symbol() + design = self.get_design(value, symbol) + corner_numbers = self.get_corner_numbers(value, symbol) + self.add(design, corner_numbers) + + def get_value(self): + value = self.value + possible_values = map(str, range(1, 11)) + ["J", "Q", "K"] + if value is None: + value = random.choice(possible_values) + value = str(value) + if value not in possible_values: + raise Exception("Invalid card value") + value = value.capitalize() + if value == "1": + value = "A" + return value + + def get_symbol(self): + suit = self.suit + possible_suits = ["hearts", "diamonds", "spades", "clubs"] + if suit is None: + suit = random.choice(possible_suits) + if suit not in possible_suits: + raise Exception("Invalud suit value") + symbol_height = float(self.height) / self.card_height_to_symbol_height + symbol = SuitSymbol(suit, height = symbol_height) + return symbol + + def get_design(self, value, symbol): + if value == "A": + return self.get_ace_design(symbol) + if value in map(str, range(2, 11)): + return self.get_number_design(value, symbol) + else: + return self.get_face_card_design(value, symbol) + + def get_ace_design(self, symbol): + design = symbol.copy().scale(1.5) + design.move_to(self) + return design + + def get_number_design(self, value, symbol): + num = int(value) + n_rows = { + 2 : 2, + 3 : 3, + 4 : 2, + 5 : 2, + 6 : 3, + 7 : 3, + 8 : 3, + 9 : 4, + 10 : 4, + }[num] + n_cols = 1 if num in [2, 3] else 2 + insertion_indices = { + 5 : [0], + 7 : [0], + 8 : [0, 1], + 9 : [1], + 10 : [0, 2], + }.get(num, []) + + top = self.get_top() + symbol.get_height()*DOWN + bottom = self.get_bottom() + symbol.get_height()*UP + column_points = [ + interpolate(top, bottom, alpha) + for alpha in np.linspace(0, 1, n_rows) + ] + + design = VGroup(*[ + symbol.copy().move_to(point) + for point in column_points + ]) + if n_cols == 2: + space = 0.2*self.get_width() + column_copy = design.copy().shift(space*RIGHT) + design.shift(space*LEFT) + design.add(*column_copy) + design.add(*[ + symbol.copy().move_to( + center_of_mass(column_points[i:i+2]) + ) + for i in insertion_indices + ]) + for symbol in design: + if symbol.get_center()[1] < self.get_center()[1]: + symbol.rotate_in_place(np.pi) + return design + + def get_face_card_design(self, value, symbol): + from topics.characters import PiCreature + sub_rect = Rectangle( + stroke_color = BLACK, + fill_opacity = 0, + height = 0.9*self.get_height(), + width = 0.6*self.get_width(), + ) + sub_rect.move_to(self) + + pi_color = average_color(symbol.get_color(), GREY) + pi_mode = { + "J" : "plain", + "Q" : "thinking", + "K" : "hooray" + }[value] + pi_creature = PiCreature( + mode = pi_mode, + color = pi_color, + ) + pi_creature.scale_to_fit_width(0.8*sub_rect.get_width()) + if value in ["Q", "K"]: + prefix = "king" if value == "K" else "queen" + crown = SVGMobject(file_name = prefix + "_crown") + crown.set_stroke(width = 0) + crown.set_fill(YELLOW, 1) + crown.stretch_to_fit_width(0.5*sub_rect.get_width()) + crown.stretch_to_fit_height(0.17*sub_rect.get_height()) + crown.move_to(pi_creature.eyes.get_center(), DOWN) + pi_creature.add_to_back(crown) + to_top_buff = 0 + else: + to_top_buff = SMALL_BUFF*sub_rect.get_height() + pi_creature.next_to(sub_rect.get_top(), DOWN, to_top_buff) + # pi_creature.shift(0.05*sub_rect.get_width()*RIGHT) + + pi_copy = pi_creature.copy() + pi_copy.rotate(np.pi, about_point = sub_rect.get_center()) + + return VGroup(sub_rect, pi_creature, pi_copy) + + def get_corner_numbers(self, value, symbol): + value_mob = TextMobject(value) + width = self.get_width()/self.card_width_to_corner_num_width + height = self.get_height()/self.card_height_to_corner_num_height + value_mob.scale_to_fit_width(width) + value_mob.stretch_to_fit_height(height) + value_mob.next_to( + self.get_corner(UP+LEFT), DOWN+RIGHT, + buff = MED_LARGE_BUFF*width + ) + value_mob.highlight(symbol.get_color()) + corner_symbol = symbol.copy() + corner_symbol.scale_to_fit_width(width) + corner_symbol.next_to( + value_mob, DOWN, + buff = MED_SMALL_BUFF*width + ) + corner_group = VGroup(value_mob, corner_symbol) + opposite_corner_group = corner_group.copy() + opposite_corner_group.rotate( + np.pi, about_point = self.get_center() + ) + + return VGroup(corner_group, opposite_corner_group) + +class SuitSymbol(SVGMobject): + CONFIG = { + "height" : 0.5, + "fill_opacity" : 1, + "stroke_width" : 0, + "red" : "#D02028", + "black" : BLACK, + } + def __init__(self, suit_name, **kwargs): + digest_config(self, kwargs) + suits_to_colors = { + "hearts" : self.red, + "diamonds" : self.red, + "spades" : self.black, + "clubs" : self.black, + } + if suit_name not in suits_to_colors: + raise Exception("Invalid suit name") + SVGMobject.__init__(self, file_name = suit_name, **kwargs) + + color = suits_to_colors[suit_name] + self.set_stroke(width = 0) + self.set_fill(color, 1) + self.scale_to_fit_height(self.height) + class Speedometer(VMobject): CONFIG = {