def __init__(self, r, g, b, a, px, py, pz, ux, uy, uz, vx, vy, vz): ''' Constructor ''' self.position = VertexBuffer(12) # Upper left self.position.add_point( Vector3(px - ux - vx, py - uy - vy, pz - uz - vz)) # upper left self.position.add_point( Vector3(px - ux + vx, py - uy + vy, pz - uz + vz)) # lower right self.position.add_point( Vector3(px + ux - vx, py + uy - vy, pz + uz - vz)) # upper right self.position.add_point( Vector3(px + ux + vx, py + uy + vy, pz + uz + vz)) self.r = r self.g = g self.b = b self.a = a
def draw_internal(self, gl): if self.render_state.night_vision_mode: return gl.glEnableClientState(gl.GL_VERTEX_ARRAY) gl.glEnableClientState(gl.GL_COLOR_ARRAY) gl.glDisableClientState(gl.GL_TEXTURE_COORD_ARRAY) gl.glEnable(gl.GL_CULL_FACE) gl.glFrontFace(gl.GL_CW) gl.glCullFace(gl.GL_BACK) gl.glShadeModel(gl.GL_SMOOTH) gl.glPushMatrix() # Rotate the sky box to the position of the sun. cp = cross_product(Vector3(0, 1, 0), self.sun_pos) cp = normalized(cp) angle = 180.0 / math.pi * math.acos(self.sun_pos.y) gl.glRotatef(angle, cp.x, cp.y, cp.z) self.vertex_buffer.set(gl) self.color_buffer.set(gl) self.index_buffer.draw(gl, gl.GL_TRIANGLES) gl.glPopMatrix()
def matrix_vector_multiply(m, v): ''' multiply m * v where m is a matrix and v is a vector ''' return Vector3(m.xx * v.x + m.xy * v.y + m.xz * v.z, m.yx * v.x + m.yy * v.y + m.yz * v.z, m.zx * v.x + m.zy * v.y + m.zz * v.z)
def vector_product(v1, v2): ''' given two vector3 objects perform the vector product ''' term1 = v1.y * v2.z - v1.z * v2.y term2 = -v1.x * v2.z + v1.z * v2.x term3 = v1.x * v2.y - v1.y * v2.x return Vector3(term1, term2, term3)
def set_view_up_direction(self, viewer_up): if abs(viewer_up.y) < 0.999: cp = cross_product(viewer_up, Vector3(0, 0, 0)) cp = normalized(cp) self.geo_to_viewer_transform = create_rotation(math.acos(viewer_up.y), cp) else: self.geo_to_viewer_transform = create_identity() self.must_update_transformed_orientation = True
def __init__(self, layer_id, new_texture_manager): ''' Constructor ''' RendererObjectManager.__init__(self, layer_id, new_texture_manager) self.width = 2 self.height = 2 self.geo_to_viewer_transform = create_identity() self.look_dir = Vector3(0, 0, 0) self.up_dir = Vector3(0, 1, 0) self.transformed_look_dir = Vector3(0, 0, 0) self.transformed_up_dir = Vector3(0, 1, 0) #search_helper = SearchHelper() self.dark_quad = None #self.search_arrow = SearchArrow() #crosshair = CrosshairOverlay()
def __init__(self, geo_coord, new_id, up_v=Vector3(0.0, 1.0, 0.0), im_scale=1): ''' Constructor ''' Source.__init__(self, colors.WHITE, geo_coord) self.is_blank = False self.requires_blending = False self.pixmap_image = None self.image_scale = im_scale self.set_up_vector(up_v) self.set_image_id(new_id)
def update_view(self, gl): # Get a vector perpendicular to both, pointing to the right, by taking # lookDir cross up. look_dir = self.render_state.look_dir.copy() up_dir = self.render_state.up_dir.copy() right = cross_product(look_dir, up_dir) if self.DEBUG_MODE != None: from src.units.Vector3 import Vector3 look_dir = Vector3(Debug.LOOKDIRVECTORS[self.DEBUG_MODE][0], Debug.LOOKDIRVECTORS[self.DEBUG_MODE][1], Debug.LOOKDIRVECTORS[self.DEBUG_MODE][2]) up_dir = Vector3(Debug.UPDIRVECTORS[self.DEBUG_MODE][0], Debug.UPDIRVECTORS[self.DEBUG_MODE][1], Debug.UPDIRVECTORS[self.DEBUG_MODE][2]) right = Vector3(Debug.RIGHTVECTORS[self.DEBUG_MODE][0], Debug.RIGHTVECTORS[self.DEBUG_MODE][1], Debug.RIGHTVECTORS[self.DEBUG_MODE][2]) self.view_matrix = Matrix4x4.create_view(look_dir, up_dir, right) gl.glMatrixMode(gl.GL_MODELVIEW) adjust_matrix = Matrix4x4.Matrix4x4([0.0, 0.0, -1.0, 0.0, 0.0, 1.0, -0.0, 0.0, 1.0, 0.0, -0.0, 0.0, 0.0, 0.0, 0.0, 1.0]) matrix = Matrix4x4.multiply_MM(self.view_matrix, adjust_matrix) # Invert the left/right rotation of the matrix matrix.values[2] *= -1 matrix.values[8] *= -1 # Invert these so that we don't rotate in and out of the unit sphere matrix.values[1] *= -1 matrix.values[4] *= -1 matrix = np.array(matrix.values, dtype=np.float32) #matrix = np.array(self.view_matrix.values, dtype=np.float32) gl.glLoadMatrixf(matrix)
def __init__(self, mag_dec_calc): ''' Constructor ''' self.auto_update_pointing = True self.field_of_view = 45 #Degrees self.location = LatLong(0, 0) self.clock = RealClock() self.celestial_coords_last_updated = -1 # provides correction from true North to magnetic North self.magnetic_declination_calc = mag_dec_calc #The pointing comprises a vector into the phone's screen expressed in #celestial coordinates combined with a perpendicular vector along the #phone's longer side. self.pointing = Pointing() #The sensor acceleration in the phone's coordinate system. self.acceleration = ApplicationConstants.INITIAL_DOWN #The sensor magnetic field in the phone's coordinate system. self.magnetic_field = ApplicationConstants.INITIAL_SOUTH #North along the ground in celestial coordinates. self.true_north_celestial = Vector3(1, 0, 0); #Up in celestial coordinates. self.up_celestial = Vector3(0, 1, 0) #East in celestial coordinates. self.true_east_celestial = self.AXIS_OF_EARTHS_ROTATION #[North, Up, East]^-1 in phone coordinates. self.axes_phone_inverse_matrix = get_identity_matrix(); #[North, Up, East] in celestial coordinates. */ self.axes_magnetic_celestial_matrix = get_identity_matrix();
def __init__(self, sky_renderer, new_layer, new_texture_manager): ''' Constructor ''' RendererObjectManager.__init__(self, new_layer, new_texture_manager) self.label_maker = None self.labels = [] self.sky_region_map = SkyRegionMap() # These are intermediate variables set in beginDrawing() and used in # draw() to make the transformations more efficient self.label_offset = Vector3(0, 0, 0) self.dot_product_threshold = None self.texture_ref = None # A quad with size 1 on each size, so we just need to multiply # by the label's width and height to get it to the right size for each # label. vertices = [ -0.5, -0.5, # lower left -0.5, 0.5, # upper left 0.5, -0.5, # lower right 0.5, 0.5 ] # upper right # make the vertices fixed point? byte buffer? self.quad_buffer = np.array(vertices, dtype=np.float32) # We want to initialize the labels of a sky region to an empty list. def construct_method(): return [] self.sky_region_map.region_data_factory = \ SkyRegionMap.RegionDataFactory(construct_method)
def draw_label(self, gl, label): look_dir = self.render_state.look_dir if look_dir.x * label.x + look_dir.y * label.y + \ look_dir.z * label.z < self.dot_product_threshold: return # Offset the label to be underneath the given position (so a label will # always appear underneath a star no matter how the phone is rotated) v = Vector3(label.x - self.label_offset.x * label.offset, label.y - self.label_offset.y * label.offset, label.z - self.label_offset.z * label.offset) screen_pos = transform_vector(self.render_state.transform_to_screen, v) # We want this to align consistently with the pixels on the screen, so we # snap to the nearest x/y coordinate, and add a magic offset of less than # half a pixel. Without this, rounding error can cause the bottom and # top of a label to be one pixel off, which results in a noticeable # distortion in the text. MAGIC_OFFSET = 0.25 screen_pos.x = int(screen_pos.x) + MAGIC_OFFSET screen_pos.y = int(screen_pos.y) + MAGIC_OFFSET gl.glPushMatrix() gl.glTranslatef(screen_pos.x, screen_pos.y, 0) gl.glRotatef((180.0 / math.pi) * self.render_state.up_angle, 0, 0, -1) gl.glScalef(label.width_in_pixels, label.height_in_pixels, 1) gl.glVertexPointer(2, gl.GL_FLOAT, 0, self.quad_buffer) gl.glTexCoordPointer(2, gl.GL_FLOAT, 0, label.text_coords) if self.render_state.night_vision_mode: gl.glColor4ub(0xFF, 0, 0, label.a) else: gl.glColor4ub(label.r, label.g, label.b, 128) gl.glDrawArrays(gl.GL_TRIANGLE_STRIP, 0, 4) gl.glPopMatrix()
def scale(v, factor): return Vector3(v.x * factor, v.y * factor, v.z * factor)
zs = axis.z * sin_d xm = axis.x * one_minus_cos_d ym = axis.y * one_minus_cos_d zm = axis.z * one_minus_cos_d xym = axis.x * ym yzm = axis.y * zm zxm = axis.z * xm return Matrix33(axis.x * xm + cos_d, xym + zs, zxm - ys, xym - zs, axis.y * ym + cos_d, yzm + xs, zxm + ys, yzm - xs, axis.z * zm + cos_d) if __name__ == "__main__": ''' For debugging purposes ''' import time import units.LatLong as LL from units.RaDec import RaDec M = calculate_rotation_matrix(90, Vector3(1, 0, 0)) print M.xx, M.xy, M.xz print M.yx, M.yy, M.yz print M.zx, M.zy, M.zz ra, dec = calculate_RADec_of_zenith(time.gmtime(), LL.LatLong(20, 16)) radec = RaDec(ra, dec) print radec.ra, radec.dec
def update_objects(self, points, update_type): #only_update_points = True # We only care about updates to positions, ignore any other updates. if self.update_type.Reset in update_type: #only_update_points = False pass elif self.update_type.UpdatePositions in update_type: # Sanity check: make sure the number of points is unchanged. if len(points) != self.num_points: return else: return self.num_points = len(points) self.sky_regions.clear() if Debug.ALLREGIONS == "YES": self.COMPUTE_REGIONS = False region = SkyRegionMap.CATCHALL_REGION_ID if self.COMPUTE_REGIONS: # Find the region for each point, and put it in a separate list # for that region. for point in points: if len(points) < self.MINIMUM_NUM_POINTS_FOR_REGIONS: region = SkyRegionMap.CATCHALL_REGION_ID else: region = self.sky_regions.get_object_region(point.geocentric_coords) # self.sky_regions.get_region_data(region) is a RegionData instance data_for_region = self.sky_regions.get_region_data(region) data_for_region.sources.append(point) else: self.sky_regions.get_region_data(region).sources = points # Generate the resources for all of the regions. for data in self.sky_regions.region_data.values(): num_vertices = 4 * len(data.sources) num_indices = 6 * len(data.sources) data.vertex_buffer.reset(num_vertices) data.color_buffer.reset(num_vertices) data.text_coord_buffer.reset(num_vertices) data.index_buffer.reset(num_indices) up = Vector3(0, 1, 0) # By inspecting the perspective projection matrix, you can show that, # to have a quad at the center of the screen to be of size k by k # pixels, the width and height are both: # k * tan(fovy / 2) / screenHeight # This is not difficult to derive. Look at the transformation matrix # in SkyRenderer if you're interested in seeing why this is true. # I'm arbitrarily deciding that at a 60 degree field of view, and 480 # pixels high, a size of 1 means "1 pixel," so calculate size_factor # based on this. These numbers mostly come from the fact that that's # what I think looks reasonable. fovy_in_radians = 60 * math.pi / 180.0 size_factor = math.tan(fovy_in_radians * 0.5) / 480 bottom_left_pos = Vector3(0, 0, 0) top_left_pos = Vector3(0, 0, 0) bottom_right_pos = Vector3(0, 0, 0) top_right_pos = Vector3(0, 0, 0) su = Vector3(0, 0, 0) sv = Vector3(0, 0, 0) index = 0 star_width_in_texels = 1.0 / self.NUM_STARS_IN_TEXTURE for p_source in data.sources: color = 0xFF000000 | int(p_source.color) # Force alpha to 0xff if Debug.COLOR == "WHITE ONLY": color = 0xFFFFFFFF bottom_left = index top_left = index + 1 bottom_right = index + 2 top_right = index + 3 index += 4 # First triangle data.index_buffer.add_index(bottom_left) data.index_buffer.add_index(top_left) data.index_buffer.add_index(bottom_right) # Second triangle data.index_buffer.add_index(top_right); data.index_buffer.add_index(bottom_right); data.index_buffer.add_index(top_left); # PointSource.getPointShape().getImageIndex(); is always 0 star_index = 0 tex_offset_u = star_width_in_texels * star_index data.text_coord_buffer.add_text_coord(tex_offset_u, 1); data.text_coord_buffer.add_text_coord(tex_offset_u, 0); data.text_coord_buffer.add_text_coord(tex_offset_u + star_width_in_texels, 1); data.text_coord_buffer.add_text_coord(tex_offset_u + star_width_in_texels, 0); pos = p_source.geocentric_coords u = normalized(cross_product(pos, up)) v = cross_product(u, pos) s = p_source.size * size_factor su.assign(s*u.x, s*u.y, s*u.z) sv.assign(s*v.x, s*v.y, s*v.z) bottom_left_pos.assign(pos.x - su.x - sv.x, pos.y - su.y - sv.y, pos.z - su.z - sv.z) top_left_pos.assign(pos.x - su.x + sv.x, pos.y - su.y + sv.y, pos.z - su.z + sv.z) bottom_right_pos.assign(pos.x + su.x - sv.x, pos.y + su.y - sv.y, pos.z + su.z - sv.z) top_right_pos.assign(pos.x + su.x + sv.x, pos.y + su.y + sv.y, pos.z + su.z + sv.z) # Add the vertices data.vertex_buffer.add_point(bottom_left_pos) data.color_buffer.add_color(color) data.vertex_buffer.add_point(top_left_pos) data.color_buffer.add_color(color) data.vertex_buffer.add_point(bottom_right_pos) data.color_buffer.add_color(color) data.vertex_buffer.add_point(top_right_pos) data.color_buffer.add_color(color) data.sources = None
class AstronomerModel(object): ''' Class responsible to modelling the astronomer This class has state on the location and orientation of the astronomer in space and performs calculations for obtaining directions and time. As stated in the android version: There are 3 frames of reference: Celestial - a frame fixed against the background stars with x, y, z axes pointing to (RA = 90, DEC = 0), (RA = 0, DEC = 0), DEC = 90 Phone - a frame fixed in the phone with x across the short side, y across the long side, and z coming out of the phone screen. Local - a frame fixed in the astronomer's local position. x is due east along the ground y is due north along the ground, and z points towards the zenith. We calculate the local frame in phone coords, and in celestial coords and calculate a transform between the two. In the following, N, E, U correspond to the local North, East and Up vectors (ie N, E along the ground, Up to the Zenith) In Phone Space: axesPhone = [N, E, U] In Celestial Space: axesSpace = [N, E, U] We find T such that axesCelestial = T * axesPhone Then, [viewDir, viewUp]_celestial = T * [viewDir, viewUp]_phone where the latter vector is trivial to calculate. ''' POINTING_DIR_IN_PHONE_COORDS = Vector3(0, 0, -1) SCREEN_UP_IN_PHONE_COORDS = Vector3(0, 1, 0) AXIS_OF_EARTHS_ROTATION = Vector3(0, 0, 1) MINIMUM_TIME_BETWEEN_CELESTIAL_COORD_UPDATES_MILLIS = 60000.0 def get_time(self): ''' return a time struct of the current GM time ''' return time.gmtime(self.clock.get_time()) def set_location(self, lat_long): self.location = lat_long self.calculate_local_north_and_up_in_celestial_coords(True) def set_phone_sensor_values(self, accel, mag_field): self.acceleration.assign(vector3=accel) self.magnetic_field.assign(vector3=mag_field) def get_north(self): ''' In celestial coordinates ''' self.calculate_local_north_and_up_in_celestial_coords(False) return get_instance_from_vector3(self.true_north_celestial) def get_south(self): ''' In celestial coordinates ''' self.calculate_local_north_and_up_in_celestial_coords(False) v = Geometry.scale_vector(self.true_north_celestial, -1) return get_instance_from_vector3(v) def get_zenith(self): ''' In celestial coordinates ''' self.calculate_local_north_and_up_in_celestial_coords(False) return get_instance_from_vector3(self.up_celestial) def get_nadir(self): ''' In celestial coordinates ''' self.calculate_local_north_and_up_in_celestial_coords(False) v = Geometry.scale_vector(self.up_celestial, -1) return get_instance_from_vector3(v) def get_east(self): ''' In celestial coordinates ''' self.calculate_local_north_and_up_in_celestial_coords(False) return get_instance_from_vector3(self.true_east_celestial) def get_west(self): ''' In celestial coordinates ''' self.calculate_local_north_and_up_in_celestial_coords(False) v = Geometry.scale_vector(self.true_east_celestial, -1) return get_instance_from_vector3(v) def set_mag_dec_calc(self, calculator): self.magnetic_declination_calc = calculator self.calculate_local_north_and_up_in_celestial_coords(True) def calculate_pointing(self): ''' update the direction that the phone is pointing and the up vector perpendicular to the phone (in celestial coords). ''' if not (self.auto_update_pointing): return transform = Geometry.matrix_multiply(self.axes_magnetic_celestial_matrix, \ self.axes_phone_inverse_matrix) view_in_space_space = \ Geometry.matrix_vector_multiply(transform, self.POINTING_DIR_IN_PHONE_COORDS) screen_up_in_space_space = \ Geometry.matrix_vector_multiply(transform, self.SCREEN_UP_IN_PHONE_COORDS) self.pointing.update_line_of_sight(view_in_space_space) self.pointing.update_perpendicular(screen_up_in_space_space) def calculate_local_north_and_up_in_celestial_coords(self, force_update): current_time = self.get_time_in_millis() diff = math.fabs(current_time - self.celestial_coords_last_updated) if (not force_update) and diff < self.MINIMUM_TIME_BETWEEN_CELESTIAL_COORD_UPDATES_MILLIS: return self.celestial_coords_last_updated = current_time self.update_magnetic_correction() up_ra, up_dec = Geometry.calculate_RADec_of_zenith(self.get_time(), self.location) self.up_celestial = get_instance(up_ra, up_dec) z = self.AXIS_OF_EARTHS_ROTATION z_dotu = Geometry.scalar_product(self.up_celestial, z) self.true_north_celestial = \ Geometry.add_vectors(z, Geometry.scale_vector(self.up_celestial, -z_dotu)) self.true_north_celestial.normalize() self.true_east_celestial = Geometry.vector_product(self.true_north_celestial, \ self.up_celestial) # Apply magnetic correction. Rather than correct the phone's axes for # the magnetic declination, it's more efficient to rotate the # celestial axes by the same amount in the opposite direction. declination = self.magnetic_declination_calc.get_declination() rotation_matrix = Geometry.calculate_rotation_matrix(declination, self.up_celestial) magnetic_north_celestial = Geometry.matrix_vector_multiply(rotation_matrix, \ self.true_north_celestial) magnetic_east_celestial = Geometry.vector_product(magnetic_north_celestial, \ self.up_celestial) self.axes_magnetic_celestial_matrix = get_colmatrix_from_vectors(magnetic_north_celestial, \ self.up_celestial, \ magnetic_east_celestial) def calculate_local_north_and_up_in_phone_coords(self): down = self.acceleration.copy() down.normalize() # Magnetic field goes *from* North to South, so reverse it. magnetic_field_to_north = self.magnetic_field.copy() magnetic_field_to_north.scale(-1) magnetic_field_to_north.normalize() # This is the vector to magnetic North *along the ground*. v2 = Geometry.scale_vector(down, -Geometry.scalar_product(magnetic_field_to_north, \ down)) magnetic_north_phone = Geometry.add_vectors(magnetic_field_to_north, v2) magnetic_north_phone.normalize() up_phone = Geometry.scale_vector(down, -1) magnetic_east_phone = Geometry.vector_product(magnetic_north_phone, up_phone) # The matrix is orthogonal, so transpose it to find its inverse. # Easiest way to do that is to construct it from row vectors instead # of column vectors. self.axes_phone_inverse_matrix = \ get_rowmatrix_from_vectors(magnetic_north_phone, up_phone, magnetic_east_phone) def update_magnetic_correction(self): self.magnetic_declination_calc.set_location_and_time(\ self.location, self.get_time_in_millis()) def get_pointing(self): self.calculate_local_north_and_up_in_phone_coords() self.calculate_pointing() return self.pointing def set_pointing(self, line_of_sight, perpendicular): ''' Takes 2 vector3 objects as parameters ''' self.pointing.update_line_of_sight(line_of_sight) self.pointing.update_perpendicular(perpendicular) def set_clock(self, clock): ''' clock must have a get_time() method ''' self.clock = clock self.calculate_local_north_and_up_in_celestial_coords(True) def get_time_in_millis(self): return int(self.clock.get_time() * 1000) def __init__(self, mag_dec_calc): ''' Constructor ''' self.auto_update_pointing = True self.field_of_view = 45 #Degrees self.location = LatLong(0, 0) self.clock = RealClock() self.celestial_coords_last_updated = -1 # provides correction from true North to magnetic North self.magnetic_declination_calc = mag_dec_calc #The pointing comprises a vector into the phone's screen expressed in #celestial coordinates combined with a perpendicular vector along the #phone's longer side. self.pointing = Pointing() #The sensor acceleration in the phone's coordinate system. self.acceleration = ApplicationConstants.INITIAL_DOWN #The sensor magnetic field in the phone's coordinate system. self.magnetic_field = ApplicationConstants.INITIAL_SOUTH #North along the ground in celestial coordinates. self.true_north_celestial = Vector3(1, 0, 0); #Up in celestial coordinates. self.up_celestial = Vector3(0, 1, 0) #East in celestial coordinates. self.true_east_celestial = self.AXIS_OF_EARTHS_ROTATION #[North, Up, East]^-1 in phone coordinates. self.axes_phone_inverse_matrix = get_identity_matrix(); #[North, Up, East] in celestial coordinates. */ self.axes_magnetic_celestial_matrix = get_identity_matrix();
def scale_vector(v3, scale): ''' return new scaled vector ''' return Vector3(scale * v3.x, scale * v3.y, scale * v3.z)
def add_vectors(v1, v2): ''' return a new vector whos coordinates are the sum of the two input vectors ''' return Vector3(v1.x + v2.x, v1.y + v2.y, v1.z + v2.z)
class MeteorRadiantSource(AbstractAstronomicalSource): ''' Meteor Showers extend AstronomicalSource so that they can be rendered in the same way as other Astronomical sources. ''' # G1 bug not present in PyOpenGL LABEL_COLOR = 0x817ef6 UP = Vector3(0.0, 1.0, 0.0) UPDATE_FREQ_MS = 1 * MILLISECONDS_PER_DAY SCALE_FACTOR = 0.03 def get_names(self): return self.search_names def get_search_location(self): return self.shower.radiant def update_shower(self): self.last_update_time_Ms = mktime(self.model.get_time()) # We will only show the shower if it's the right time of year. now = self.model.get_time() # Standardize on the same year as we stored for the showers. now = dt.datetime(MeteorShowerLayer.ANY_OLD_YEAR, now.tm_mon, now.tm_mday, 0, 0, 0).timetuple() self.the_image.set_up_vector(self.UP) if mktime(self.shower.start) < mktime(now) and \ mktime(self.shower.end) > mktime(now): self.label.label = self.name percent_to_peak = 0 if (mktime(now) < mktime(self.shower.peak)): percent_to_peak = (mktime(now) - mktime(self.shower.start)) / \ (mktime(self.shower.peak) - mktime(self.shower.start)) else: percent_to_peak = (mktime(self.shower.end) - mktime(now)) / \ (mktime(self.shower.end) - mktime(self.shower.peak)) # Not sure how best to calculate number of meteors - use linear interpolation for now. number_of_meteors_per_hour = self.shower.peak_meteors_per_hour * percent_to_peak if number_of_meteors_per_hour > MeteorShowerLayer.METEOR_THRESHOLD_PER_HR: self.the_image.set_image_id("meteor2_screen") else: self.the_image.set_image_id("meteor1_screen") else: self.label.label = " " self.the_image.set_image_id("blank") def initialize(self): self.update_shower() return self #override(AbstractAstronomicalSource) def update(self): update_types = set() if abs(mktime(self.model.get_time()) - self.last_update_time_Ms) > self.UPDATE_FREQ_MS: self.update_shower() update_types = set([RendererObjectManager().update_type.Reset]) return update_types #override(AbstractAstronomicalSource) def get_images(self): return self.image_sources #override(AbstractAstronomicalSource) def get_labels(self): return self.label_sources def __init__(self, model, shower): ''' constructor ''' AbstractAstronomicalSource.__init__(self) self.image_sources = [] self.label_sources = [] self.lastUpdateTimeMs = 0 self.search_names = [] self.model = model self.shower = shower self.name = shower.name_id start_date = datetime.fromtimestamp(mktime( shower.start)).strftime('%m/%d') end_date = datetime.fromtimestamp(mktime( shower.end)).strftime('%m/%d') self.search_names.append(self.name + " (" + start_date + "-" + end_date + ")") # blank is a 1pxX1px image that should be invisible. # We'd prefer not to show any image except on the shower dates, but there # appears to be a bug in the renderer/layer interface in that Update values are not # respected. Ditto the label. self.the_image = ImageSource(shower.radiant, "blank", self.UP, self.SCALE_FACTOR) self.image_sources.append(self.the_image) self.label = TextSource(self.name, self.LABEL_COLOR, shower.radiant) self.label_sources.append(self.label)
def sum_vectors(v1, v2): return Vector3(v1.x + v2.x, v1.y + v2.y, v1.z + v2.z)
def negate(v): return Vector3(-v.x, -v.y, -v.z)
def update_objects(self, image_sources, update_types): ''' takes a list of image sources and creates new buffers for these sources. ''' # hack to get rid of blank meteor showers. image_sources = [img for img in image_sources if not img.is_blank] if self.update_type.Reset not in update_types and \ len(image_sources) != len(self.images): return self.updates = self.updates | update_types # set union num_vertices = len(image_sources) * 4 vb = self.vertex_buffer vb.reset(num_vertices) tcb = self.text_coord_buffer tcb.reset(num_vertices) images = [] reset = (self.update_type.Reset in update_types) or (self.update_type.UpdateImages in update_types) if reset: images = [None] * len(image_sources) else: images = self.images if reset: for i in range(0, len(image_sources)): ims = image_sources[i] images[i] = self.Image() images[i].name = "no url" images[i].use_blending = False images[i].pixmap = ims.pixmap_image # Update the positions in the position and tex coord buffers. if reset or self.update_type.UpdatePositions in update_types: for i in range(0, len(image_sources)): ims = image_sources[i] xyz = ims.geocentric_coords px = xyz.x py = xyz.y pz = xyz.z u = ims.get_horizontal_corner() ux = u[0] uy = u[1] uz = u[2] v = ims.get_verical_corner() vx = v[0] vy = v[1] vz = v[2] # lower left vb.add_point(Vector3(px - ux - vx, py - uy - vy, pz - uz - vz)) tcb.add_text_coord(0, 1) # upper left vb.add_point(Vector3(px - ux + vx, py - uy + vy, pz - uz + vz)) tcb.add_text_coord(0, 0) # lower right vb.add_point(Vector3(px + ux - vx, py + uy - vy, pz + uz - vz)) tcb.add_text_coord(1, 1) # upper right vb.add_point(Vector3(px + ux + vx, py + uy + vy, pz + uz + vz)) tcb.add_text_coord(1, 0) # We already set the image in reset, so only set them here if we're # not doing a reset. if self.update_type.UpdateImages in update_types: for i in range(0, len(image_sources)): ims = image_sources[i] images[i].pixmap = ims.pixmap_image self.images = images self.queue_for_reload(False)
def multiply_MV(mat, v): m = mat.values[:] return Vector3(m[0] * v.x + m[4] * v.y + m[8] * v.z + m[12], m[1] * v.x + m[5] * v.y + m[9] * v.z + m[13], m[2] * v.x + m[6] * v.y + m[10] * v.z + m[14])
// language and additionally, may contain components and ideas that are // not found in the original source code. Copyright 2013 Neil Borle and Paul Lu Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. Created on 2013-05-22 @author: Neil Borle ''' from src.units.Vector3 import Vector3 APP_NAME = "SkyPython" #Default value for 'south' in phone coords when the app starts INITIAL_SOUTH = Vector3(0, -1, 0) #Default value for 'down' in phone coords when the app starts INITIAL_DOWN = Vector3(0, -1, -9)
class PlanetSource(AbstractAstronomicalSource): ''' An extension of AstronomicalSource for planets so that the elements of each planet can be rendered in a standard way. ''' PLANET_SIZE = 3 PLANET_COLOR = 0x14817EF6 # The G1 rendering bug does not appear in this python code. PLANET_LABEL_COLOR = 0x817EF6 SHOW_PLANETARY_IMAGES = "show_planetary_images" UP = Vector3(0.0, 1.0, 0.0) def get_names(self): return self.names.append(self.name) def update_coords(self, t_struct): self.last_update_time_Ms = time.mktime(t_struct) p = Planet(planet_enum.SUN, res[planet_enum.SUN][0], res[planet_enum.SUN][1], res[planet_enum.SUN][2]) self.sun_coords = get_instance(t_struct=t_struct, planet=p) ra_dec = radec_get_instance(earth_coord=self.sun_coords, planet=self.planet, time=t_struct) self.current_coords.update_from_ra_dec(ra_dec.ra, ra_dec.dec) for imgsrc in self.image_sources: imgsrc.set_up_vector(self.sun_coords) def initialize(self): time = self.model.get_time() self.update_coords(time) self.image_id = self.planet.get_image_resource_id(time) if self.planet.id == planet_enum.MOON: self.image_sources.append( ImageSource(self.current_coords, self.image_id, self.sun_coords, self.planet.get_planetary_image_size())) else: if USE_PLANETARY_IMAGES or self.planet.id == planet_enum.SUN: self.image_sources.append( ImageSource(self.current_coords, self.image_id, self.UP, self.planet.get_planetary_image_size())) else: self.point_sources.append( PointSource(self.PLANET_COLOR, self.PLANET_SIZE, self.current_coords)) self.label_sources.append( TextSource(self.name, self.PLANET_LABEL_COLOR, self.current_coords)) return self #override(AbstractAstronomicalSource) def update(self): updates = set() model_time = self.model.get_time() if abs(time.mktime(model_time) - self.last_update_time_Ms) > self.planet.get_update_frequency_Ms: updates = updates | set( [RendererObjectManager().update_type.UpdatePositions]) # update location self.update_coords(model_time) # For moon only: if self.planet.id == planet_enum.MOON and self.image_sources != []: # Update up vector. self.image_sources[0].set_up_vector(self.sun_coords) # update image: new_image_id = self.planet.get_image_resource_id(model_time) if new_image_id != self.image_id: self.image_id = new_image_id self.image_sources[0].image_id = new_image_id updates = updates | set( [RendererObjectManager().update_type.UpdateImages]) return updates #override(AbstractAstronomicalSource) def get_points(self): return self.point_sources #override(AbstractAstronomicalSource) def get_labels(self): return self.label_sources #override(AbstractAstronomicalSource) def get_images(self): return self.image_sources def __init__(self, planet, model): ''' Constructor ''' AbstractAstronomicalSource.__init__(self) self.point_sources = [] self.image_sources = [] self.label_sources = [] self.planet = planet self.model = model self.name = planet.name_resource_id self.current_coords = GeocentricCoordinates(0, 0, 0) self.sun_coords = None self.image_id = -1 self.last_update_time_Ms = 0
def zero(): return Vector3(0, 0, 0)
def cross_product(p1, p2): return Vector3(p1.y * p2.z - p1.z * p2.y, -p1.x * p2.z + p1.z * p2.x, p1.x * p2.y - p1.y * p2.x)
def __init__(self, layer_id, texture_manager): ''' Constructor ''' RendererObjectManager.__init__(self, layer_id, texture_manager) self.vertex_buffer = VertexBuffer(0, True) self.color_buffer = ColorBuffer(0, True) self.index_buffer = IndexBuffer(0, True) self.sun_pos = GeocentricCoordinates(0, 1, 0) num_vertices = self.NUM_VERTEX_BANDS * self.NUM_STEPS_IN_BAND num_indices = (self.NUM_VERTEX_BANDS - 1) * self.NUM_STEPS_IN_BAND * 6 self.vertex_buffer.reset(num_vertices) self.color_buffer.reset(num_vertices) self.index_buffer.reset(num_indices) sin_angles = [0.0] * self.NUM_STEPS_IN_BAND cos_angles = [0.0] * self.NUM_STEPS_IN_BAND angle_in_band = 0 d_angle = 2 * math.pi / float(self.NUM_STEPS_IN_BAND - 1) for i in range(0, self.NUM_STEPS_IN_BAND): sin_angles[i] = math.sin(angle_in_band) cos_angles[i] = math.cos(angle_in_band) angle_in_band += d_angle band_step = 2.0 / float((self.NUM_VERTEX_BANDS - 1) + self.EPSILON) vb = self.vertex_buffer cb = self.color_buffer band_pos = 1 for band in range(0, self.NUM_VERTEX_BANDS): a, r, g, b = 0, 0, 0, 0 if band_pos > 0: intensity = long(band_pos * 20 + 50) & 0xFFFFFFFF a = 0xFF r = (intensity << 16) & 0x00FF0000 g = (intensity << 16) & 0x0000FF00 b = (intensity << 16) & 0x000000FF else: intensity = long(band_pos * 40 + 40) & 0xFFFFFFFF color = (intensity << 16) | (intensity << 8) | (intensity) a = 0xFF r = color & 0x00FF0000 g = color & 0x0000FF00 b = color & 0x000000FF band_pos -= band_step sin_phi = math.sqrt(1 - band_pos * band_pos) if band_pos > -1 else 0 for i in range(0, self.NUM_STEPS_IN_BAND): vb.add_point( Vector3(cos_angles[i] * sin_phi, band_pos, sin_angles[i] * sin_phi)) cb.add_color(a, r, g, b) ib = self.index_buffer # Set the indices for the first band. top_band_start = 0 bottom_band_start = self.NUM_STEPS_IN_BAND for triangle_band in range(0, self.NUM_VERTEX_BANDS - 1): for offset_from_start in range(0, self.NUM_STEPS_IN_BAND - 1): # Draw one quad as two triangles. top_left = (top_band_start + offset_from_start) top_right = (top_left + 1) bottom_left = (bottom_band_start + offset_from_start) bottom_right = (bottom_left + 1) # First triangle ib.add_index(top_left) ib.add_index(bottom_right) ib.add_index(bottom_left) # Second triangle ib.add_index(top_right) ib.add_index(bottom_right) ib.add_index(top_left) # Last quad: connect the end with the beginning. # Top left, bottom right, bottom left ib.add_index((top_band_start + self.NUM_STEPS_IN_BAND - 1)) ib.add_index(bottom_band_start) ib.add_index((bottom_band_start + self.NUM_STEPS_IN_BAND - 1)) # Top right, bottom right, top left ib.add_index(top_band_start) ib.add_index(bottom_band_start) ib.add_index((top_band_start + self.NUM_STEPS_IN_BAND - 1)) top_band_start += self.NUM_STEPS_IN_BAND bottom_band_start += self.NUM_STEPS_IN_BAND