예제 #1
0
class ListedArea(BaseWidget):
    name = 'Astronomical Objects'

    def __init__(self, parent, x, y, w, h):
        super().__init__(parent)
        self.image = Surface((w, h))
        self.image.fill(COLOR_AREA)
        self.rect = self.image.get_rect(topleft=(x, y))
        self.listed_objects = WidgetGroup()

        self.f = self.crear_fuente(14, underline=True)
        self.write(self.name,
                   self.f,
                   midtop=(self.rect.w / 2, 0),
                   bg=COLOR_AREA)

    def populate(self, objects):
        return NotImplemented

    def hide(self):
        for listed in self.listed_objects.widgets():
            listed.hide()
        super().hide()

    def on_mousebuttondown(self, event):
        if event.button == 1:
            self.deselect_all()

    def delete_objects(self, astronomical_object):
        for listed in self.listed_objects.widgets():
            if listed.object_data is astronomical_object:
                listed.kill()
                break
        self.sort()

    def sort(self):
        for i, listed in enumerate(self.listed_objects.widgets()):
            listed.rect.y = i * 16 + self.rect.y + 21

    def select_one(self, it):
        for listed in self.listed_objects.widgets():
            listed.deselect()
        it.select()

    def deselect_all(self):
        for listed in self.listed_objects.widgets():
            listed.deselect()

    def objects(self):
        return [o.object_data for o in self.listed_objects.widgets()]

    def show_current(self, idx):
        for listed in self.listed_objects.widgets():
            listed.hide()
        for listed in self.listed_objects.get_widgets_from_layer(idx):
            listed.show()

    def update(self):
        self.image.fill(COLOR_AREA, (0, 17, self.rect.w, self.rect.h - 17))
        self.show_current(Systems.get_current_idx())

    def __len__(self):
        return len(self.listed_objects)
예제 #2
0
class AsteroidType(BaseWidget):
    current = None
    has_values = False
    locked = False

    def __init__(self, parent):
        super().__init__(parent)
        self.properties = WidgetGroup()
        self.create()
        EventHandler.register(self.clear, 'ClearData')
        self.relative_args = ['density', 'mass', 'volume']

    def create(self):
        for i, prop in enumerate(["Density", "Mass", "Volume"]):
            vt = ValueText(self, prop, 50, 64 + i * 48, COLOR_TEXTO, COLOR_BOX)
            self.properties.add(vt, layer=2)

        for i, prop in enumerate(["A Axis", "B Axis", "C Axis", "Shape"],
                                 start=4):
            vt = ValueText(self, prop, 50, 52 + i * 40, COLOR_TEXTO, COLOR_BOX)
            self.properties.add(vt, layer=3)
            vt.modifiable = True

        for i, name in enumerate(sorted(material_densities)):
            a = ValueText(self,
                          name.capitalize(),
                          3,
                          420 + 21 + i * 21,
                          bg=COLOR_AREA)
            self.properties.add(a, layer=4)
            a.modifiable = True

    def calculate(self):
        data = {'composition': None}
        if self.current is None:
            data['composition'] = {}
        else:
            data['composition'] = self.current.composition

        for item in self.properties.get_widgets_from_layer(2):
            if item.text_area.value:
                data[item.text.lower()] = float(item.text_area.value)

        for item in self.properties.get_widgets_from_layer(3):
            if item.text_area.value:
                data[item.text.lower()] = float(item.text_area.value)

        for material in self.properties.get_widgets_from_layer(4):
            if material.text_area.value:  # not empty
                data['composition'][material.text.lower()] = float(
                    material.text_area.value)

        if self.current is not None:
            data['a axis'] = self.current.a_axis.m
            data['b axis'] = self.current.b_axis.m
            data['c axis'] = self.current.c_axis.m
            data['id'] = self.current.id
            data['system'] = self.current.system_id

        moon = minor_moon_by_composition(data)
        if self.current is None:
            if Systems.get_current().add_astro_obj(moon):
                self.current = moon
                self.parent.button_add.enable()
        else:
            Systems.get_current().remove_astro_obj(self.current)
            if Systems.get_current().add_astro_obj(moon):
                self.current = moon

        if self.current.system_id is None:
            self.current.system_id = Systems.get_current().id

        self.fill()

    def clear(self, event):
        if event.data['panel'] is self.parent:
            self.erase()

    def erase(self):
        self.current = None
        self.has_values = False
        for vt in self.properties:
            vt.value = ''

    def destroy_button(self):
        destroyed = Systems.get_current().remove_astro_obj(self.current)
        if destroyed:
            self.parent.del_button(self.current)
            self.erase()

    def show_current(self, asteroid):
        self.erase()
        self.current = asteroid
        self.calculate()

    def fill(self):
        tos = {'Mass': 'Me', 'Density': 'De', 'Volume': 'Ve'}

        for elemento in self.properties.get_widgets_from_layer(2):
            idx = self.properties.widgets().index(elemento)
            attr = self.relative_args[idx]

            if not self.parent.relative_mode:
                got_attr = getattr(self.current, attr)
            else:
                got_attr = getattr(self.current,
                                   attr).to(tos[elemento.text.capitalize()])
            attr = q(str(got_attr.m),
                     got_attr.u) if type(got_attr) is not str else got_attr
            elemento.value = attr
            if elemento.text_area.unit == 'earth_mass':
                elemento.do_round = False
            elemento.text_area.show()

        for elemento in self.properties.get_widgets_from_layer(3):
            name = elemento.text
            if ' ' in elemento.text:
                name = name.replace(' ', '_')
            got_attr = getattr(self.current, name.lower())
            attr = q(str(round(got_attr.m, 3)),
                     got_attr.u) if type(got_attr) is not str else got_attr
            elemento.value = attr
            elemento.text_area.show()

        for elemento in self.properties.get_widgets_from_layer(4):
            got_attr = self.current.composition.get(elemento.text.lower(), 0)
            attr = str(round(got_attr, 3)) + ' %'
            elemento.value = attr
            elemento.text_area.show()

        self.has_values = True

    def show(self):
        for p in self.properties.widgets():
            p.show()

    def hide(self):
        for p in self.properties.widgets():
            p.hide()
예제 #3
0
class StarPanel(BasePanel):
    curr_x = 3
    curr_y = 440

    add_on_exit = False

    def __init__(self, parent):
        super().__init__('Star', parent)
        self.properties = WidgetGroup()
        self.current = StarType(self)
        self.area_buttons = self.image.fill(COLOR_AREA,
                                            [0, 420, self.rect.w, 200])
        f = self.crear_fuente(14, underline=True)
        render = f.render('Stars', True, COLOR_TEXTO, COLOR_AREA)
        self.image.blit(render, self.area_buttons.topleft)
        self.button_add = AddStarButton(self, ANCHO - 13, 398)
        self.button_del = DelStarButton(self, ANCHO - 13, 416)
        self.properties.add(self.button_add, self.button_del, layer=1)
        self.stars = []
        EventHandler.register(self.save_stars, 'Save')
        EventHandler.register(self.load_stars, 'LoadData')
        EventHandler.register(self.name_current, 'NameObject')

    @property
    def star_buttons(self):
        # adds readability
        return self.properties.get_widgets_from_layer(2)

    def save_stars(self, event):
        data = []
        for star_button in self.star_buttons:
            star = star_button.object_data
            star_data = {
                'name': star.name,
                'mass': star.mass.m,
                'id': star.id,
                'spin': star.spin
            }
            data.append(star_data)
        EventHandler.trigger(event.tipo + 'Data', 'Star', {"Stars": data})

    def load_stars(self, event):
        for idx, star_data in enumerate(event.data.get('Stars', [])):
            star_data.update({'idx': idx})
            star = Star(star_data)
            if star not in self.stars:
                self.stars.append(star)
                self.add_button(star)

        if len(self.star_buttons):
            self.current.current = self.star_buttons[0].object_data

    def show(self):
        super().show()
        for obj in self.properties.widgets():
            obj.show()
        if self.current.has_values:
            self.current.current.sprite.show()

    def hide(self):
        super().hide()
        for obj in self.properties.widgets():
            obj.hide()
        if self.add_on_exit:
            self.parent.set_skippable('Star System', True)
            Systems.set_system(self.current.current)
        else:
            self.parent.set_skippable('Star System', False)

    def add_button(self, star):
        button = StarButton(self.current, star, self.curr_x, self.curr_y)
        self.properties.add(button, layer=2)
        Systems.add_star(star)
        if star not in self.stars:
            self.stars.append(star)
        self.sort_buttons()
        self.current.erase()
        self.button_add.disable()

    def del_button(self, planet):
        button = [i for i in self.star_buttons if i.object_data == planet][0]
        self.properties.remove(button)
        self.sort_buttons()
        self.button_del.disable()
        self.stars.remove(button.object_data)

    def sort_buttons(self):
        x, y = self.curr_x, self.curr_y
        for bt in self.star_buttons:
            bt.move(x, y)
            if not self.area_buttons.contains(bt.rect):
                bt.hide()
            else:
                bt.show()
            if x + bt.rect.w + 10 < self.rect.w - bt.rect.w + 10:
                x += bt.rect.w + 10
            else:
                x = 3
                y += 32

    def select_one(self, btn):
        for button in self.star_buttons:
            button.deselect()
        btn.select()

    def on_mousebuttondown(self, event):
        if event.button == 1:
            super().on_mousebuttondown(event)

        elif event.button in (4, 5):
            buttons = self.star_buttons
            if self.area_buttons.collidepoint(event.pos) and len(buttons):
                last_is_hidden = not buttons[-1].is_visible
                first_is_hidden = not buttons[0].is_visible
                if event.button == 4 and first_is_hidden:
                    self.curr_y += 32
                elif event.button == 5 and last_is_hidden:
                    self.curr_y -= 32
                self.sort_buttons()

    def update(self):
        self.add_on_exit = len(self.stars) == 1

    def name_current(self, event):
        if event.data['object'] in self.stars:
            star = event.data['object']
            star.name = event.data['name']
            star.has_name = True
예제 #4
0
class PlanetaryOrbitPanel(BaseWidget):
    skippable = True
    skip = False
    current = None
    markers = None
    satellites = None

    curr_digit = 0
    selected_marker = None

    curr_x = 0
    curr_y = 0

    added = None
    visible_markers = True

    def __init__(self, parent):
        super().__init__(parent)
        self.name = 'Planetary Orbit'
        self.image = Surface((ANCHO, ALTO - 32))
        self.image.fill(COLOR_BOX)
        self.rect = self.image.get_rect()
        self.properties = WidgetGroup()
        self.buttons = WidgetGroup()
        self.orbit_descriptions = WidgetGroup()
        self._markers = {}
        self.markers = []
        self.added = []
        self.objects = []
        self.satellites = {}
        self._loaded_orbits = []
        self.area_buttons = self.image.fill(COLOR_AREA,
                                            [0, 420, self.rect.w, 200])
        self.area_markers = Rect(3, 58, 380, 20 * 16)
        self.curr_x = self.area_buttons.x + 3
        self.curr_y = self.area_buttons.y + 21
        self.planet_area = AvailablePlanets(self, ANCHO - 200, 32, 200, 340)
        self.add_orbits_button = SetOrbitButton(self, ANCHO - 94, 394)
        self.area_modify = ModifyArea(self, ANCHO - 201, 374)
        self.show_markers_button = ToggleableButton(self, 'Satellites',
                                                    self.toggle_stellar_orbits,
                                                    3, 421)
        self.show_markers_button.disable()
        self.resonances_button = AddResonanceButton(self, ANCHO - 140, 416)
        self.order_f = self.crear_fuente(14)
        self.write(self.name + ' Panel',
                   self.crear_fuente(16, underline=True),
                   centerx=(ANCHO // 4) * 1.5,
                   y=0)
        self.digit_x = RatioDigit(self, 'x',
                                  self.resonances_button.rect.left - 60,
                                  self.resonances_button.rect.y)
        self.write(':',
                   self.crear_fuente(16),
                   topleft=[
                       self.digit_x.rect.right + 1,
                       self.resonances_button.rect.y - 1
                   ])
        self.digit_y = RatioDigit(self, 'y', self.digit_x.rect.right + 9,
                                  self.resonances_button.rect.y)
        self.ratios = [self.digit_x, self.digit_y]
        self.cycler = cycle(self.ratios)
        next(self.cycler)

        self.properties.add(self.area_modify,
                            self.show_markers_button,
                            self.digit_x,
                            self.digit_y,
                            self.planet_area,
                            self.add_orbits_button,
                            self.resonances_button,
                            layer=2)

        EventHandler.register(self.save_orbits, 'Save')
        EventHandler.register(self.load_orbits, 'LoadData')

    def load_orbits(self, event):
        for position in event.data.get('Planetary Orbits', []):
            if position not in self._loaded_orbits:
                self._loaded_orbits.append(position)

    def set_loaded_orbits(self):
        for orbit_data in self._loaded_orbits:
            a = q(orbit_data['a'], 'earth_radius')
            e = q(orbit_data['e'])
            i = q(orbit_data['i'], 'degree')
            system = Systems.get_system_by_id(orbit_data['star_id'])
            planet = system.get_astrobody_by(orbit_data['planet_id'],
                                             tag_type='id')
            if planet.id not in self.satellites:
                self.satellites[planet.id] = []

            if planet.id not in self._markers:
                self._markers[planet.id] = []
            satellite = system.get_astrobody_by(orbit_data['astrobody'],
                                                tag_type='id')
            self.satellites[planet.id].append(satellite)
            satellite.set_orbit(planet, [a, e, i])
            self.add_existing(satellite, planet.id)

        # borrar las órbitas cargadas para evitar que se dupliquen.
        self._loaded_orbits.clear()

    def save_orbits(self, event):
        orbits = self._loaded_orbits
        for planet_obj in self.planet_area.listed_objects.widgets():
            planet = planet_obj.object_data
            for marker in self._markers.get(planet.id, []):
                if marker.orbit is not None:
                    d = self.create_save_data(marker.orbit)
                    orbits.append(d)

        EventHandler.trigger(event.tipo + 'Data', 'Orbit',
                             {'Planetary Orbits': orbits})

    @staticmethod
    def create_save_data(orb):
        d = {}
        if hasattr(orb, 'semi_major_axis'):
            d['a'] = round(orb.semi_major_axis.m, 2)
        if hasattr(orb, 'inclination'):
            d['i'] = orb.inclination.m
        if hasattr(orb, 'eccentricity'):
            d['e'] = orb.eccentricity.m
        if hasattr(orb, 'astrobody'):
            d['astrobody'] = orb.astrobody.id
            d['planet_id'] = orb.astrobody.parent.id
            d['star_id'] = orb.astrobody.parent.parent.id
        return d

    def toggle_stellar_orbits(self):
        if self.visible_markers:
            self.area_modify.color_alert()
            self.add_orbits_button.disable()
            for marker in self.markers:
                marker.hide()
        else:
            for button in self.buttons.widgets():
                button.deselect()

            self.hide_orbit_types()
            if self.current is not None:
                for marker in self.markers:
                    marker.show()
            self.show_markers_button.disable()
            self.area_modify.color_standby()
        self.visible_markers = not self.visible_markers
        self.area_modify.visible_markers = self.visible_markers

    def hide_orbit_types(self):
        for orbit_type in self.orbit_descriptions.widgets():
            orbit_type.hide()
        for orbit_button in self.buttons.widgets():
            orbit_button.enable()

    def populate(self):
        planet = self.current
        if planet.id not in self._markers:
            self._markers[planet.id] = []
        self.markers = self._markers[planet.id]
        for marker in self.markers:
            if marker not in self.properties:
                self.properties.add(marker, layer=3)
            marker.show()

        self.create_hill_marker(planet)
        self.sort_markers()

    def add_objects(self):
        system = Systems.get_current()
        if system is not None:
            for obj in system.satellites + system.asteroids:
                if obj not in self.objects:
                    self.objects.append(obj)
                    btn = ObjectButton(self, obj, self.curr_x, self.curr_y)
                    if obj.orbit is not None:
                        btn.update_text(obj.orbit.a)
                        markers = self._markers[obj.orbit.star.id]
                        marker_idx = [
                            i for i in range(len(markers))
                            if markers[i].obj == obj
                        ][0]
                        marker = markers[marker_idx]
                        btn.link_marker(marker)

                    self.buttons.add(btn, layer=Systems.get_current_idx())
                    self.properties.add(btn)
            self.sort_buttons()

    def show(self):
        super().show()
        for prop in self.properties.get_widgets_from_layer(2):
            prop.show()
        self.set_loaded_orbits()
        self.add_objects()

    def hide(self):
        super().hide()
        for prop in self.properties.widgets():
            prop.hide()

    def select_planet(self, planet):
        if planet is not self.current:
            self.hide_everything()
            self.current = planet
            self.populate()
            if planet.id not in self.satellites:
                self.satellites[planet.id] = []
        for button in self.buttons.widgets():
            button.enable()
            button.deselect()

        self.visible_markers = True
        sats = self.satellites[planet.id]
        densest = sorted(sats,
                         key=lambda i: i.density.to('earth_density').m,
                         reverse=True)
        if len(densest):
            self.create_roches_marker(densest[0])
        self.sort_markers()

    def select_one(self, button):
        for bttn in self.buttons.widgets():
            bttn.deselect()
        button.select()

    def anchor_maker(self, marker):
        self.area_modify.link(marker)
        self.area_modify.visible_markers = True
        self.add_orbits_button.link(marker)
        self.add_orbits_button.enable()
        self.selected_marker = marker

    def deselect_markers(self, m):
        for marker in self.markers:
            marker.deselect()
            marker.enable()
        m.select()

    def sort_markers(self):
        self.markers.sort(key=lambda m: m.value.m)
        for i, marker in enumerate(self.markers, start=1):
            marker.rect.y = i * 2 * 10 + 38
            if not self.area_markers.contains(marker.rect):
                marker.hide()
            else:
                marker.show()

    def sort_buttons(self):
        x, y = self.curr_x, self.curr_y
        for bt in self.buttons.get_widgets_from_layer(
                Systems.get_current_idx()):
            bt.move(x, y)
            if not self.area_buttons.contains(bt.rect):
                bt.hide()
            else:
                bt.show()
            if x + bt.rect.w + 10 < self.rect.w - bt.rect.w + 10:
                x += bt.rect.w + 10
            else:
                x = 3
                y += 32

    def create_roches_marker(self, obj):
        obj_density = obj.density.to('earth_density').m
        roches = self.current.set_roche(obj_density)
        roches_marker = Marker(self, "Roche's Limit", roches, lock=True)
        first = self.markers[0]
        if first.name == "Roche's Limit":
            self.properties.remove(first)
            self.markers[0] = roches_marker
        else:
            self.markers.append(roches_marker)
        self.properties.add(roches_marker, layer=3)
        return roches

    def create_hill_marker(self, planet):
        x = Marker(self, 'Hill Sphere', planet.hill_sphere)
        x.locked = True
        last = None if not len(self.markers) else self.markers[-1]
        if last is not None and last.name == 'Hill Sphere':
            self.properties.remove(last)
            self.markers[-1] = x
        else:
            self.markers.append(x)
        self.properties.add(x, layer=3)

    def add_new(self, obj):
        if obj not in self.added:
            self.added.append(obj)
        obj_name = obj.cls
        pln_habitable = Systems.get_current().is_habitable(self.current)
        pln_hill = self.current.hill_sphere.m
        obj_type = obj.celestial_type
        roches = self.create_roches_marker(obj)

        text = "A satellite's mass must be less than or equal to the\nmass of the planet."
        text += '\n\nConsider using a less massive satellite for this planet.'
        assert self.current.mass.to('earth_mass').m >= obj.mass.to(
            'earth_mass').m, text

        pos = q(
            round(
                roll(self.current.roches_limit.m,
                     self.current.hill_sphere.m / 2), 3), 'earth_radius')
        orbit = RawOrbit(Systems.get_current_star(), pos)
        obj_marker = Marker(self,
                            obj_name,
                            pos,
                            color=COLOR_SELECTED,
                            lock=False)

        max_value = pln_hill
        if pln_habitable and obj_type != 'asteroid':
            max_value /= 2
        obj_marker.set_max_value(max_value)
        obj_marker.set_min_value(roches.m)
        obj_marker.links(orbit, obj)

        self.markers.append(obj_marker)
        self.properties.add(obj_marker, layer=3)
        self.sort_markers()

        return orbit, obj_marker

    def add_existing(self, obj, pln_id):
        if obj not in self.added:
            self.added.append(obj)
        obj_name = obj.cls
        orbit = obj.orbit
        pos = orbit.a
        obj_marker = Marker(self,
                            obj_name,
                            pos,
                            color=COLOR_SELECTED,
                            lock=False)
        obj_marker.links(orbit, obj)
        self._markers[pln_id].append(obj_marker)

    def hide_markers(self):
        for marker in self.markers:
            marker.hide()
        self.show_markers_button.enable()

    def hide_everything(self):
        for marker in self.markers:
            if marker.linked_button is not None:
                marker.linked_button.hide_info()
            marker.hide()
        self.visible_markers = False
        self.show_markers_button.disable()

    def is_added(self, obj):
        return obj in self.added

    def get_raw_orbit_markers(self):
        raws = [
            m for m in self.markers
            if ((not m.locked) and (type(m.orbit) == RawOrbit))
        ]
        return raws

    def link_satellite_to_planet(self, marker):
        marker._orbit = PseudoOrbit(marker.orbit)
        button = marker.linked_button
        self.hide_everything()
        button.update_text(marker.orbit.a)
        button.info.link_marker(marker)
        button.info.locked = False
        button.info.show()

    def notify(self):
        if not self.visible_markers:
            self.show_markers_button.enable()
            for button in self.buttons.widgets():
                button.disable()

    def get_sorted_satellites(self):
        self.sort_markers()
        markers = [
            marker.obj for marker in self.markers if marker.obj is not None
        ]
        return markers

    def set_current_digit(self, idx):
        self.curr_digit = self.ratios.index(idx)

    def cycle(self):
        has_values = False
        for ratio in self.ratios:
            ratio.deselect()
            has_values = ratio.value != ''

        valid = has_values and self.selected_marker is not None

        if valid:
            self.resonances_button.enable()
        else:
            ratio = next(self.cycler)
            ratio.select()
            WidgetHandler.set_origin(ratio)

    def ratios_to_string(self):
        x = int(self.digit_x.value)
        y = int(self.digit_y.value)
        diff = y - x if y > x else x - y
        self.write('{}° Order'.format(diff),
                   self.order_f,
                   right=self.digit_x.rect.left - 2,
                   y=self.digit_x.rect.y)
        return '{}:{}'.format(x, y)

    def get_difference(self):
        x = int(self.digit_x.value)
        y = int(self.digit_y.value)
        return x - y

    def clear_ratios(self):
        self.digit_x.clear()
        self.digit_y.clear()
예제 #5
0
class AsteroidPanel(BasePanel):
    curr_x = 0
    curr_y = 0
    mass_number = None
    loaded_data = None
    last_idx = None

    def __init__(self, parent):
        super().__init__('Asteroid', parent)
        self.properties = WidgetGroup()
        self.current = AsteroidType(self)
        f1 = self.crear_fuente(16, underline=True)
        f2 = self.crear_fuente(13, underline=True)
        r = self.image.fill(COLOR_AREA, [0, 420, (self.rect.w // 4) + 32, 200])
        self.write('Composition', f1, COLOR_AREA, topleft=(0, 420))
        self.area_asteroids = self.image.fill(COLOR_AREA,
                                              (r.right + 10, r.y, 400, 200))
        self.write('Asteroids',
                   f2,
                   COLOR_AREA,
                   x=self.area_asteroids.x + 3,
                   y=self.area_asteroids.y)
        self.curr_x = self.area_asteroids.x + 3
        self.curr_y = self.area_asteroids.y + 21
        self.properties.add(self.current)
        self.button_add = AddAsteroidButton(self, ANCHO - 13, 398)
        self.button_del = DelAsteroidButton(self, ANCHO - 13, 416)
        self.properties.add(self.button_add, self.button_del)
        self.asteroids = WidgetGroup()
        self.moons = []
        EventHandler.register(self.load_satellites, 'LoadData')
        EventHandler.register(self.save_satellites, 'Save')
        EventHandler.register(self.name_current, 'NameObject')

    def name_current(self, event):
        if event.data['object'] in self.moons:
            moon = event.data['object']
            moon.name = event.data['name']
            moon.has_name = True

    def load_satellites(self, event):
        if 'Asteroids' in event.data and len(event.data['Asteroids']):
            self.loaded_data = event.data['Asteroids']

    def show_loaded(self):
        if self.loaded_data is not None:
            for satellite_data in self.loaded_data:
                moon = minor_moon_by_composition(satellite_data)
                system = Systems.get_system_by_id(satellite_data['system'])
                if system.add_astro_obj(moon):
                    self.current.current = moon
                    self.add_button()
            self.loaded_data.clear()

    def save_satellites(self, event):
        data = []
        for moon_button in self.asteroids.widgets():
            moon = moon_button.object_data
            moon_data = {
                'name': moon.name,
                'a axis': moon.a_axis.m,
                'b axis': moon.b_axis.m,
                'c axis': moon.c_axis.m,
                'composition': moon.composition,
                'id': moon.id,
                'system': moon.system_id
            }
            data.append(moon_data)
            EventHandler.trigger(event.tipo + 'Data', 'Planet',
                                 {"Asteroids": data})

    def add_button(self):
        button = AsteroidButton(self.current, self.current.current,
                                self.curr_x, self.curr_y)
        if self.current.current.system_id is not None:
            layer_number = Systems.get_system_idx_by_id(
                self.current.current.system_id)
        else:
            layer_number = Systems.get_current_idx()
            self.current.current.system_id = Systems.get_current().id
        self.moons.append(self.current.current)
        self.asteroids.add(button, layer=layer_number)
        self.properties.add(button)
        self.sort_buttons()
        self.current.erase()
        self.button_add.disable()

    def del_button(self, satellite):
        button = [
            i for i in self.asteroids.widgets() if i.object_data == satellite
        ][0]
        self.moons.remove(satellite)
        self.asteroids.remove(button)
        self.sort_buttons()
        self.properties.remove(button)
        self.button_del.disable()

    def show_current(self, idx):
        for button in self.asteroids.widgets():
            button.hide()
        for button in self.asteroids.get_widgets_from_layer(idx):
            button.show()
        self.sort_buttons()

    def select_one(self, btn):
        for button in self.asteroids.widgets():
            button.deselect()
        btn.select()

    def sort_buttons(self):
        x, y = self.curr_x, self.curr_y
        for bt in self.asteroids.get_widgets_from_layer(
                Systems.get_current_idx()):
            bt.move(x, y)
            if not self.area_asteroids.contains(bt.rect):
                bt.hide()
            else:
                bt.show()
            if x + bt.rect.w + 10 < self.rect.w - bt.rect.w + 10:
                x += bt.rect.w + 10
            else:
                x = 3
                y += 32

    def show(self):
        super().show()
        self.show_loaded()
        self.is_visible = True
        if self.mass_number is None:
            self.properties.add(ShownMass(self))
        for pr in self.properties.widgets():
            pr.show()

    def hide(self):
        super().hide()
        self.is_visible = False
        for pr in self.properties.widgets():
            pr.hide()

        flag = Systems.get_current() is not None
        flag = not len(Systems.get_current().asteroids +
                       Systems.get_current().satellites) if flag else False
        self.parent.set_skippable('Planetary Orbit', flag)

    def update(self):
        idx = Systems.get_current_idx()
        if idx != self.last_idx:
            self.show_current(idx)
            self.last_idx = idx
예제 #6
0
class PlanetPanel(BasePanel):
    curr_x = 0
    curr_y = 440
    unit = None
    is_visible = False
    last_idx = None
    mass_number = None

    def __init__(self, parent):
        super().__init__('Planet', parent)
        self.area_buttons = self.image.fill(COLOR_AREA,
                                            [0, 420, self.rect.w, 200])
        self.current = PlanetType(self)
        self.properties = WidgetGroup()

        self.unit = Unit(self, 0, 416)
        self.properties.add(self.unit)

        self.button_add = AddPlanetButton(self, ANCHO - 13, 398)
        self.button_del = DelPlanetButton(self, ANCHO - 13, 416)
        self.properties.add(self.button_add, self.button_del)
        self.planet_buttons = WidgetGroup()
        self.planets = []
        EventHandler.register(self.save_planets, 'Save')
        EventHandler.register(self.name_current, 'NameObject')

    def save_planets(self, event):
        data = self.current.loaded_data if self.current.loaded_data is not None else []
        for system in Systems.get_systems():
            for planet in self.planets:
                if planet in system.planets:
                    planet_data = {
                        'name': planet.name,
                        'mass': planet.mass.m,
                        'radius': planet.radius.m,
                        'unit': planet.unit,
                        'atmosphere': planet.atmosphere,
                        'composition': planet.composition,
                        'clase': planet.clase,
                        'system': system.id,
                        'id': planet.id,
                        'albedo': planet.albedo.m,
                        'tilt': planet.tilt.m
                    }
                    data.append(planet_data)
        EventHandler.trigger(event.tipo + 'Data', 'Planet', {"Planets": data})

    def add_button(self, planet):
        button = CreatedPlanet(self.current, planet, self.curr_x, self.curr_y)
        if planet.system_id is not None:
            layer_number = Systems.get_system_idx_by_id(planet.system_id)
        else:
            layer_number = Systems.get_current_idx()
            planet.system_id = Systems.get_current().id
        self.planet_buttons.add(button, layer=layer_number)
        self.planets.append(planet)
        self.sort_buttons()
        self.properties.add(button, layer=3)

    def del_button(self, planet):
        button = [
            i for i in self.planet_buttons.widgets() if i.object_data == planet
        ][0]
        self.planet_buttons.remove(button)
        self.planets.remove(planet)
        self.sort_buttons()
        self.properties.remove(button)
        self.button_del.disable()

    def show_current(self, idx):
        for button in self.planet_buttons.widgets():
            button.hide()
        for button in self.planet_buttons.get_widgets_from_layer(idx):
            button.show()
        self.sort_buttons()

    def sort_buttons(self):
        x, y = self.curr_x, self.curr_y
        for bt in self.planet_buttons.get_widgets_from_layer(
                Systems.get_current_idx()):
            bt.move(x, y)
            if not self.area_buttons.contains(bt.rect):
                bt.hide()
            else:
                bt.show()
            if x + bt.rect.w + 10 < self.rect.w - bt.rect.w + 10:
                x += bt.rect.w + 10
            else:
                x = 3
                y += 32

    def select_one(self, btn=None):
        for button in self.planet_buttons.widgets():
            button.deselect()

        if btn is not None:
            btn.select()

    def on_mousebuttondown(self, event):
        if event.button == 1:
            super().on_mousebuttondown(event)

        elif event.button in (4, 5):
            buttons = self.planet_buttons.widgets()
            if self.area_buttons.collidepoint(event.pos) and len(buttons):
                last_is_hidden = not buttons[-1].is_visible
                first_is_hidden = not buttons[0].is_visible
                if event.button == 4 and first_is_hidden:
                    self.curr_y += 32
                elif event.button == 5 and last_is_hidden:
                    self.curr_y -= 32
                self.sort_buttons()

    def show(self):
        super().show()
        if self.mass_number is None:
            self.properties.add(ShownMass(self))
        props = self.properties.get_widgets_from_layer(1)
        props += self.properties.get_widgets_from_layer(2)
        for item in props:
            item.show()
        if self.last_idx is not None:
            self.show_current(self.last_idx)

    def hide(self):
        super().hide()
        for item in self.properties.widgets():
            item.hide()

    def update(self):
        idx = Systems.get_current_idx()
        if idx != self.last_idx:
            self.show_current(idx)
            self.last_idx = idx

    def name_current(self, event):
        if event.data['object'] in self.planets:
            planet = event.data['object']
            planet.name = event.data['name']
            planet.has_name = True
예제 #7
0
class SystemType(BaseWidget):
    locked = False
    has_values = False
    current = None

    def __init__(self, parent):
        super().__init__(parent)
        self.properties = WidgetGroup()
        self.primary = None
        self.secondary = None
        self.separation = None
        self.ecc_p = None
        self.ecc_s = None
        self.create()
        EventHandler.register(self.clear, 'ClearData')

    def create(self):
        props = [
            'Primary Star', 'Secondary Star', 'Average Separation',
            'Eccentriciy (primary)', 'Eccentricty (secondary)', 'Barycenter',
            'Maximun Separation', 'Minimun Separation',
            'Forbbiden Zone Inner edge', 'Forbbiden Zone Outer edge',
            'System Type', 'System Name'
        ]

        for i, prop in enumerate([j for j in props]):
            vt = ValueText(self, prop, 3, 64 + i * 25, COLOR_TEXTO, COLOR_BOX)
            self.properties.add(vt)
            if i in [2, 3, 4]:
                vt.modifiable = True

        attrs = ['primary', 'secondary', 'separation', 'ecc_p', 'ecc_s']
        for idx, attr in enumerate(attrs):
            setattr(self, attr, self.properties.get_sprite(idx))

    def set_star(self, star):
        if str(self.primary.value) == '':
            self.primary.value = star
        elif star.spin == self.primary.value.spin:
            spin = 'clockwise' if star.spin == 'counter-clockwise' else 'counter-clockwise'
            raise AssertionError(
                'The stars must spin\nin oposite directions\nas they would collide\notherwise.\n'
                f'\nSelect a star\nthat spins {spin}.')
        else:
            self.secondary.value = star
            self.parent.restore_button.enable()

    def unset_stars(self):
        self.parent.stars_area.populate(Systems.loose_stars)
        self.erase()

    def get_determinants(self):
        names = ['primary', 'secondary', 'separation', 'ecc_p', 'ecc_s']
        dets = [self.primary.value, self.secondary.value]
        return dets + [
            float(getattr(self, name).value)
            for name in names if name not in ('primary', 'secondary')
        ]

    def fill(self):
        if all([str(vt.value) != '' for vt in self.properties.widgets()[0:5]]):
            if self.current is None:
                self.current = system_type(
                    self.separation.value)(*self.get_determinants())
            props = [
                'average_separation', 'ecc_p', 'ecc_s', 'barycenter',
                'max_sep', 'min_sep', 'inner_forbbiden_zone',
                'outer_forbbiden_zone', 'system_name', 'name'
            ]
            for i, attr in enumerate(props, start=2):
                value = getattr(self.current, attr)
                pr = self.properties.get_widget(i)
                pr.value = value
            self.parent.setup_button.enable()

    def reset(self, system_data):
        self.set_star(system_data.primary)
        self.set_star(system_data.secondary)
        self.separation.value = system_data.average_separation
        self.ecc_p.value = system_data.ecc_p
        self.ecc_s.value = system_data.ecc_s
        self.fill()

    def clear(self, event):
        if event.data['panel'] is self.parent:
            self.erase()

    def erase(self):
        for button in self.properties.widgets():
            button.text_area.clear()
        self.has_values = False
        self.current = None

    def destroy(self):
        self.parent.del_button(self.current)
        self.erase()

    def show(self):
        for prop in self.properties.widgets():
            prop.show()

    def hide(self):
        for prop in self.properties.widgets():
            prop.hide()
예제 #8
0
class StarSystemPanel(BaseWidget):
    selected = None
    curr_x = 0
    curr_y = 440
    skip = False
    skippable = True

    def __init__(self, parent):
        super().__init__(parent)
        self.name = 'Star System'
        self.image = Surface((ANCHO, ALTO - 32))
        self.image.fill(COLOR_BOX)
        self.rect = self.image.get_rect()
        self.area_buttons = self.image.fill(COLOR_AREA,
                                            [0, 420, self.rect.w, 200])
        self.f2 = self.crear_fuente(14, underline=True)
        self.write('Star Systems', self.f2, COLOR_AREA, x=3, y=420)
        self.properties = WidgetGroup()
        self.f1 = self.crear_fuente(16, underline=True)
        self.write(self.name + ' Panel',
                   self.f1,
                   centerx=(ANCHO // 4) * 1.5,
                   y=0)
        self.stars_area = AvailableStars(self, ANCHO - 200, 32, 200, 340)

        self.current = SystemType(self)

        self.systems = []
        self.setup_button = SetupButton(self, 484, 416)
        self.undo_button = DissolveButton(self, 334, 416)
        self.restore_button = UndoButton(self, 234, 416)
        self.properties.add(self.setup_button, self.undo_button,
                            self.restore_button, self.stars_area, self.current)
        self.system_buttons = WidgetGroup()
        EventHandler.register(self.save_systems, 'Save')
        EventHandler.register(self.load_systems, 'LoadData')
        EventHandler.register(self.name_current, 'NameObject')

    def name_current(self, event):
        if event.data['object'] in self.systems:
            system = event.data['object']
            system.name = event.data['name']
            system.has_name = True

    def set_current(self, system_data):
        self.current.reset(system_data)

    def show_current(self, star):
        self.current.erase()
        self.current.current = star
        self.current.reset(star)

    def create_button(self, system_data):
        if system_data not in self.systems:
            idx = len(self.systems)
            button = SystemButton(self, system_data, idx, self.curr_x,
                                  self.curr_y)
            self.systems.append(system_data)
            self.system_buttons.add(button)
            self.properties.add(button)
            self.sort_buttons()
            return button

    def sort_buttons(self):
        x, y = self.curr_x, self.curr_y
        for bt in self.system_buttons.widgets():
            bt.move(x, y)
            if not self.area_buttons.contains(bt.rect):
                bt.hide()
            else:
                bt.show()
            if x + bt.rect.w + 10 < self.rect.w - bt.rect.w + 10:
                x += bt.rect.w + 10
            else:
                x = 3
                y += 32

    def save_systems(self, event):
        data = []
        for button in self.system_buttons.widgets():
            current = button.object_data
            d = {
                'primary': current.primary.id,
                'secondary': current.secondary.id,
                'avg_s': current.average_separation.m,
                'ecc_p': current.ecc_p.m,
                "ecc_s": current.ecc_s.m,
                "id": current.id,
                "name": current.name
            }
            data.append(d)

        EventHandler.trigger(event.tipo + 'Data', 'Systems', {'Systems': data})

    def load_systems(self, event):
        for system_data in event.data.get('Systems', []):
            avg_s = system_data['avg_s']
            ecc_p = system_data['ecc_p']
            ecc_s = system_data['ecc_s']
            prim = Systems.get_star_by_id(system_data['primary'])
            scnd = Systems.get_star_by_id(system_data['secondary'])
            idx = system_data['id']

            system = system_type(avg_s)(prim,
                                        scnd,
                                        avg_s,
                                        ecc_p,
                                        ecc_s,
                                        id=idx)
            button = self.create_button(system)
            button.hide()
            Systems.set_system(system)

    def select_one(self, btn):
        for button in self.system_buttons.widgets():
            button.deselect()
        btn.select()

    def del_button(self, system):
        button = [
            i for i in self.system_buttons.widgets() if i.object_data == system
        ][0]
        self.systems.remove(system)
        self.system_buttons.remove(button)
        self.sort_buttons()
        self.properties.remove(button)
        self.undo_button.disable()

    def show(self):
        for system in Systems.get_systems():
            self.create_button(system.star_system)
        super().show()
        for prop in self.properties.widgets():
            prop.show()

    def hide(self):
        super().hide()
        for prop in self.properties.widgets():
            prop.hide()
        if len(self.systems) or len(self.stars_area):
            for s in self.systems + self.stars_area.objects():
                Systems.set_system(s)
예제 #9
0
class OrbitType(BaseWidget, Intertwined):
    linked_astrobody = None
    locked = True

    modifiable = True
    has_values = False

    def __init__(self, parent):
        super().__init__(parent)
        self.properties = WidgetGroup()

    def link_astrobody(self, astro):
        self.linked_astrobody = astro
        self.locked = False

    def get_orbit(self):
        orbit = self.linked_marker.orbit
        orbit = orbit if not hasattr(orbit,
                                     'astrobody') else orbit.astrobody.orbit
        return orbit

    def create(self):
        orbit = self.get_orbit()
        self.clear()
        props = [
            'Semi-major axis', 'Semi-minor axis', 'Eccentricity',
            'Inclination', 'Periapsis', 'Apoapsis', 'Orbital motion',
            'Temperature', 'Orbital velocity', 'Orbital period',
            'Argument of periapsis', 'Longitude of the ascending node',
            'True anomaly', 'Body'
        ]
        attr = [
            'semi_major_axis', 'semi_minor_axis', 'eccentricity',
            'inclination', 'periapsis', 'apoapsis', 'motion', 'temperature',
            'velocity', 'period', 'argument_of_periapsis',
            'longitude_of_the_ascending_node', 'true_anomaly', 'astrobody'
        ]
        modifiables = [
            'Semi-major axis', 'Eccentricity', 'Inclination',
            'Argument of periapsis', 'Longitude of the ascending node'
        ]
        for i, prop in enumerate([j for j in attr if hasattr(orbit, j)]):
            value = getattr(orbit, prop)
            vt = ValueText(self, props[attr.index(prop)], 3, 64 + i * 21,
                           COLOR_TEXTO, COLOR_BOX)
            vt.value = value
            vt.modifiable = props[attr.index(prop)] in modifiables
            self.properties.add(vt)

    def fill(self):
        parametros = []
        for elemento in self.properties.widgets():
            if elemento.text == 'Inclination':
                value = q(
                    0 if elemento.text_area.value == '' else
                    elemento.text_area.value, 'degree')
            elif elemento.text not in ['Orbital motion', 'Temperature']:
                value = q(elemento.text_area.value)
            else:
                value = 'au'
            parametros.append(value)
        main = self.parent.current
        orbit = self.linked_astrobody.set_orbit(main, parametros)
        self.linked_marker.orbit = orbit
        self.show()
        self.parent.planet_area.delete_objects(self.linked_astrobody)
        if hasattr(self.parent, 'recomendation'):
            self.parent.recomendation.hide()
        self.locked = True
        self.has_values = True

    def clear(self):
        for prop in self.properties.widgets():
            prop.kill()
            prop.text_area.kill()

    def show(self):
        self.create()

        for p in self.properties.widgets():
            p.show()

    def hide(self):
        self.clear()

    @staticmethod
    def elevate_changes(key, new_value):
        if key == 'Eccentricity':
            if new_value.m < 0.001:
                return q(0.001)
            elif new_value.m > 0.9999:
                return q(0.999)

        elif key == 'Inclination':
            if new_value.m < 0:
                return q(0, new_value.u)
            elif new_value.m > 180:
                return q(180, new_value.u)
예제 #10
0
class OrbitPanel(BaseWidget):
    current = None  # ahora será la estrella o sistema seleccionado.
    curr_idx = None  # ahora será el layer de self.Buttons.
    selected_marker = None

    last_idx = 0
    _loaded_orbits = None

    offset = 0
    curr_x, curr_y = 3, 442
    curr_digit = 0

    visible_markers = True
    orbits = None
    markers = None
    buttons = None

    skippable = False

    no_star_error = False

    def __init__(self, parent):
        super().__init__(parent)
        self.name = 'Orbit'
        self.image = Surface((ANCHO, ALTO - 32))
        self.image.fill(COLOR_BOX)
        self.rect = self.image.get_rect()
        self.properties = WidgetGroup()
        self.area_buttons = self.image.fill(COLOR_AREA,
                                            [0, 420, self.rect.w, 200])
        self.area_markers = Rect(3, 58, 380, 20 * 16)
        self.area_scroll = Rect(3, 32, 387, 388)
        self.area_modify = ModifyArea(self, ANCHO - 201, 374)

        self.f = self.crear_fuente(16, underline=True)
        self.order_f = self.crear_fuente(14)
        self.write(self.name + ' Panel',
                   self.f,
                   centerx=(ANCHO // 4) * 1.5,
                   y=0)
        self.planet_area = AvailablePlanets(self, ANCHO - 200, 32, 200, 340)
        self.recomendation = Recomendation(self, 80, ALTO // 2 - 130)

        self._loaded_orbits = []
        self.indexes = []

        self._orbits = {}
        self._buttons = {}
        self._markers = {}

        self.orbit_descriptions = WidgetGroup()
        self.show_markers_button = ToggleableButton(self, 'Stellar Orbits',
                                                    self.toggle_stellar_orbits,
                                                    3, 421)
        self.show_markers_button.disable()
        self.add_orbits_button = AddOrbitButton(self, ANCHO - 94, 394)
        self.resonances_button = AddResonanceButton(self, ANCHO - 140, 416)

        self.digit_x = RatioDigit(self, 'x',
                                  self.resonances_button.rect.left - 60,
                                  self.resonances_button.rect.y)
        self.write(':',
                   self.crear_fuente(16),
                   topleft=[
                       self.digit_x.rect.right + 1,
                       self.resonances_button.rect.y - 1
                   ])
        self.digit_y = RatioDigit(self, 'y', self.digit_x.rect.right + 9,
                                  self.resonances_button.rect.y)
        self.ratios = [self.digit_x, self.digit_y]
        self.cycler = cycle(self.ratios)
        next(self.cycler)

        self.properties.add([
            self.area_modify, self.planet_area, self.show_markers_button,
            self.add_orbits_button, self.resonances_button, self.digit_x,
            self.digit_y
        ],
                            layer=2)
        EventHandler.register(self.clear, 'ClearData')
        EventHandler.register(self.save_orbits, 'Save')
        EventHandler.register(self.load_orbits, 'LoadData')

    def set_current(self):
        self.toggle_current_markers_and_buttons(False)
        star = Systems.get_current_star()
        self.current = star
        self.curr_idx = self.indexes.index(star)
        self.orbits = self._orbits[star]
        self.markers = self._markers[star]
        self.buttons = self._buttons[star]
        if not len(self.markers) or not self.markers[0].locked:
            self.populate()
        self.toggle_current_markers_and_buttons(True)
        self.sort_buttons()
        self.add_orbits_button.enable()
        self.visible_markers = False
        self.toggle_stellar_orbits()

    def populate(self):
        star = self.current
        markers = {
            'Inner Boundary': star.inner_boundry,
            'Habitable Inner': star.habitable_inner,
            'Habitable Outer': star.habitable_outer,
            'Frost Line': star.frost_line,
            'Outer Boundary': star.outer_boundry
        }

        for marker in markers:
            x = OrbitMarker(self, marker, star, markers[marker])
            x.locked = True
            self._markers[star].append(x)
            self.properties.add(x, layer=4)

        if hasattr(star, 'habitable_orbit'):
            markers = {
                'Inner Forbbiden Zone': star.inner_forbbiden_zone,
                'Outer Forbbiden Zone': star.outer_forbbiden_zone
            }
            for marker in markers:
                x = OrbitMarker(self, marker, star, markers[marker])
                x.locked = True
                self._markers[star].append(x)
                self.properties.add(x, layer=4)
            self.add_orbit_marker(star.habitable_orbit)

        self.sort_markers()

    def toggle_current_markers_and_buttons(self, toggle: bool):
        if self.markers is not None:
            for marker in self.markers:
                marker.toggle(toggle)
        if self.buttons is not None:
            for button in self.buttons:
                button.toggle(toggle)

    def add_orbit_marker(self, position, resonance=False):
        star = self.current if not hasattr(position, 'star') else position.star
        inner = star.inner_boundry
        outer = star.outer_boundry
        bc = False if resonance is False else True
        if type(position) is q:
            ba = True
            bb = False
            if not resonance:
                test = inner < position < outer
                color = COLOR_TEXTO
            else:
                test = inner < position  # TNOs orbit well outside of 40AUs.
                color = (255, 0, 0)  # color provisorio
        else:  # type(position) is PlanetOrbit
            ba = False
            bb = True
            test = True  # saved orbits are valid by definition
            color = COLOR_STARORBIT

        if test is True:
            new = OrbitMarker(self,
                              'Orbit',
                              star,
                              position,
                              is_orbit=ba,
                              is_complete_orbit=bb,
                              is_resonance=bc)
            self._markers[star].append(new)
            self._orbits[star].append(new)
            self.sort_markers()
            self.add_button_and_type(star, new, color)
            self.properties.add(new, layer=4)

    def add_button_and_type(self, star, marker, color):
        orbit_type = OrbitType(self)
        button = OrbitButton(self, color)
        self._buttons[star].append(button)

        # Buttons, OrbitTypes and Markers are all Intertwined.
        orbit_type.intertwine(m=marker, b=button)
        button.intertwine(m=marker, o=orbit_type)
        marker.intertwine(o=orbit_type, b=button)

        self.orbit_descriptions.add(orbit_type)
        if len(self.buttons):
            self.sort_buttons()
        self.properties.add(button, layer=4)
        self.properties.add(orbit_type, layer=4)
        button.enable()

    def sort_markers(self):
        self.markers.sort(key=lambda m: m.value)
        for i, marker in enumerate(self.markers, start=1):
            marker.rect.y = i * 2 * 10 + 38 + self.offset
            if not self.area_markers.contains(marker.rect):
                marker.hide()
            else:
                marker.show()

    def sort_buttons(self):
        x, y = self.curr_x, self.curr_y
        for bt in sorted(self.buttons, key=lambda b: b.get_value().m):
            bt.move(x, y)
            if not self.area_buttons.contains(bt.rect):
                bt.hide()
            else:
                bt.show()
            if x + bt.rect.w + 15 < self.rect.w - bt.rect.w + 15:
                x += bt.rect.w + 15
            else:
                x = 3
                y += 32

    def delete_marker(self, marker):
        """
        :type marker: OrbitMarker
        """
        if not marker.locked:
            marker.kill()
            marker.linked_type.kill()
            marker.linked_button.kill()
            if marker is self.area_modify.marker:
                self.area_modify.unlink()
            idx = self.markers.index(marker)
            del self.markers[idx]
            self.buttons.remove(marker.linked_button)
            self.sort_markers()
            self.sort_buttons()

    def on_mousebuttondown(self, event):

        if self.area_scroll.collidepoint(event.pos):
            last_is_hidden = not self.markers[-1].is_visible
            if len(self.markers) > 16 and event.button in (4, 5):
                if event.button == 4 and self.offset < 0:
                    self.offset += 20
                elif event.button == 5 and last_is_hidden:
                    self.offset -= 20

                self.sort_markers()

        elif self.area_buttons.collidepoint(
                event.pos) and self.buttons is not None and len(self.buttons):
            self.buttons.sort(key=lambda b: b.get_value().m)
            last_is_hidden = not self.buttons[-1].is_visible
            first_is_hidden = not self.buttons[0].is_visible
            if event.button == 4 and first_is_hidden:
                self.curr_y += 32
            elif event.button == 5 and last_is_hidden:
                self.curr_y -= 32
            self.sort_buttons()

        elif event.button == 1 and self.markers is not None:
            for marker in self.markers:
                marker.deselect()
                marker.enable()

    def check_orbits(self):
        self.orbits.sort(key=lambda o: o.value.m)
        for x, p in enumerate(self.orbits[1:], start=1):
            a = self.orbits[x - 1].value.m if x > 0 and len(
                self.orbits) else self.orbits[0].value.m  # el anterior
            assert a + 0.15 < p.value.m, 'Orbit @' + str(
                p.value.m) + ' is too close to Orbit @' + str(a)

            if x + 1 < len(self.orbits):
                b = self.orbits[x + 1].value.m  # el posterior
                assert p.value.m < b - 0.15, 'Orbit @' + str(
                    p.value.m) + ' is too close to Orbit @' + str(b)

    def anchor_maker(self, marker):
        self.area_modify.link(marker)
        self.selected_marker = marker

    def clear(self, event):
        if event.data['panel'] is self:
            for marker in self.markers:
                marker.kill()
            for orbit in self.buttons:
                orbit.kill()
            self.markers.clear()
            self.clear_ratios()

    def save_orbits(self, event):
        orbits = self._loaded_orbits
        for system in Systems.get_systems():
            if system.star_system.letter == 'S':
                for star in system:
                    for marker in self._orbits.get(star, []):
                        d = self.create_save_data(marker.orbit)
                        orbits.append(d)
            else:
                star = system.star_system
                for marker in self._orbits.get(star, []):
                    d = self.create_save_data(marker.orbit)
                    orbits.append(d)

        EventHandler.trigger(event.tipo + 'Data', 'Orbit',
                             {'Stellar Orbits': orbits})

    @staticmethod
    def create_save_data(orb):
        d = {}
        if hasattr(orb, 'semi_major_axis'):
            d['a'] = round(orb.semi_major_axis.m, 2)
        if hasattr(orb, 'inclination'):
            d['i'] = orb.inclination.m
        if hasattr(orb, 'eccentricity'):
            d['e'] = orb.eccentricity.m
        if hasattr(orb, 'astrobody'):
            d['astrobody'] = orb.astrobody.id
            d['star_id'] = orb.astrobody.orbit.star.id
        return d

    def load_orbits(self, event):
        for position in event.data.get('Stellar Orbits', []):
            if position not in self._loaded_orbits:
                self._loaded_orbits.append(position)

    def set_loaded_orbits(self):
        for orbit_data in self._loaded_orbits:
            a = q(orbit_data['a'], 'au')
            if 'e' not in orbit_data:
                self.add_orbit_marker(a)
            else:
                e = q(orbit_data['e'])
                i = q(orbit_data['i'], 'degree')
                system = Systems.get_system_by_id(orbit_data['star_id'])
                planet = system.get_astrobody_by(orbit_data['astrobody'],
                                                 tag_type='id')
                star = system.star_system
                planet.set_orbit(star, [a, e, i])
                self.add_orbit_marker(planet.orbit)
                self.planet_area.delete_objects(planet)

        # borrar las órbitas cargadas para evitar que se dupliquen.
        self.sort_markers()
        self._loaded_orbits.clear()

    def fill_indexes(self):
        assert len(Systems.get_systems())
        for system in Systems.get_systems():
            star = system.star_system
            if star not in self._markers:
                self._markers[star] = []
                self._orbits[star] = []
                self._buttons[star] = []
                self.indexes.append(star)

    def show(self):
        try:
            self.fill_indexes()
            self.set_current()
            self.no_star_error = False

        except AssertionError:
            self.no_star_error = True

        for prop in self.properties.get_widgets_from_layer(2):
            prop.show()
        self.visible_markers = True
        if len(self._loaded_orbits):
            self.set_loaded_orbits()
        self.show_markers_button.show()

        super().show()

    def hide(self):
        super().hide()
        for item in self.properties.widgets():
            item.hide()

    def toggle_stellar_orbits(self):
        if self.visible_markers:
            self.area_modify.color_alert()
            self.add_orbits_button.disable()
            for marker in self.markers:
                marker.hide()
        else:
            for marker in self.markers:
                marker.show()
            self.hide_orbit_types()
            self.show_markers_button.disable()
            self.add_orbits_button.enable()
            self.area_modify.color_standby()
        self.visible_markers = not self.visible_markers
        self.area_modify.visible_markers = self.visible_markers

    def hide_orbit_types(self):
        for orbit_type in self.orbit_descriptions.widgets():
            orbit_type.hide()
        for orbit_button in self.buttons:
            orbit_button.unlock()

    def deselect_markers(self, m):
        for marker in self.markers:
            marker.deselect()
            marker.enable()
        m.select()

    def link_astrobody_to_stellar_orbit(self, astrobody):
        locked = [i for i in self.buttons if i.locked]
        if len(locked):
            orbit = PseudoOrbit(locked[0].linked_marker.orbit)
            locked[0].linked_marker.orbit = orbit
            locked[0].linked_type.show()
            locked[0].linked_type.link_astrobody(astrobody)
            self.add_orbits_button.disable()
            if astrobody.celestial_type == 'planet':
                self.recomendation.suggest(astrobody, orbit,
                                           Systems.get_current_star())
                self.recomendation.show_suggestion(astrobody,
                                                   orbit.temperature)

    def update(self):
        super().update()
        idx = Systems.get_current_idx()
        if idx != self.last_idx:
            self.set_current()
            self.last_idx = idx

        if not self.no_star_error:
            self.image.fill(COLOR_BOX, self.area_markers)
        else:
            f = self.crear_fuente(16)
            text = 'There is no star system set. Go back to the Star Panel and set a star first.'
            rect = Rect(50, 100, 200, 100)
            render = render_textrect(text, f, rect.w, (0, 0, 0), COLOR_BOX)
            self.image.blit(render, rect)

    def __repr__(self):
        return 'Orbit Panel'

    def set_current_digit(self, idx):
        self.curr_digit = self.ratios.index(idx)

    def cycle(self):
        has_values = False
        for ratio in self.ratios:
            ratio.deselect()
            has_values = ratio.value != ''

        valid = has_values and not self.no_star_error
        valid = valid and self.selected_marker is not None

        if valid:
            self.resonances_button.enable()
        else:
            ratio = next(self.cycler)
            ratio.select()
            WidgetHandler.set_origin(ratio)

    def ratios_to_string(self):
        x = int(self.digit_x.value)
        y = int(self.digit_y.value)
        assert x >= y, 'invalid ratio'
        self.write('{}° Order'.format(x - y),
                   self.order_f,
                   right=self.digit_x.rect.left - 2,
                   y=self.digit_x.rect.y)
        return '{}:{}'.format(x, y)

    def clear_ratios(self):
        self.digit_x.clear()
        self.digit_y.clear()