class Model: """Immutable""" def __init__( self, size, observer, position = None, has_grid = False ): self.font_colour = ( 0, 0, 0 ) self.width, self.height = size if None == position: self.position = Point( 0, 0, 0 ) else: self.position = position self.observer = observer self.grid = self.make_squares( 10, ( 0, 0 ), ( self.width, self.width ), 10 ) self.font = pygame.font.SysFont( "monospace", 12 ) self.has_grid = has_grid if self.has_grid: self.front_grid = self.make_grid( ( self.width, self.width ), 50 ) self.highlight_colour = ( 255, 255, 0 ) self.highlight_radius = 5 def make_grid( self, size, sep ): width, height = size grid = [] for i in range( sep, width, sep ): grid.append( Polygon( ( 255, 255, 255 ) ).add( Point( i, 0, 0 ) ).add( Point( i, height, 0 ) ) ) for i in range( sep, height, sep ): grid.append( Polygon( ( 255, 255, 255 ) ).add( Point( 0, i, 0 ) ).add( Point( width, i, 0 ) ) ) return grid def set_grid( self, has_grid ): return Model( ( self.width, self.height ), self.observer, self.position, has_grid ) def move( self, new_position ): return Model( ( self.width, self.height ), self.observer, new_position, self.has_grid ) def move_up( self, n ): return self.move( self.position.move_up( n ) ) def move_right( self, n ): return self.move( self.position.move_right( n ) ) def move_down( self, n ): return self.move( self.position.move_down( n ) ) def move_left( self, n ): return self.move( self.position.move_left( n ) ) def move_forward( self, n ): return self.move( self.position.move_forward( n ) ) def move_back( self, n ): return self.move( self.position.move_back( n ) ) def update_position( self, position ): return Model( ( self.width, self.height ), self.observer, position, self.has_grid ) def update_observer( self, observer ): return Model( ( self.width, self.height ), observer, self.position, self.has_grid ) def snap( self, pos ): a = 50 b = 10 p = pos % a if p < b: return pos - p elif p > a - b: return pos + ( a - p ) else: return pos def output( self, screen, mode, polygons ): screen.fill( 0 ) self.output_polygons( screen, self.grid, 1 ) self.output_polygons( screen, polygons, 0 ) if len( polygons ) > 0 and polygons[ -1 ].num_points() > 0: last = polygons[ -1 ] p = last.get_points()[ -1 ] if p.z - self.position.z < self.observer.z: x, y = Point( p.x - self.position.x, p.y - self.position.y, p.z - self.position.z ).rel_to( self.observer ) colour = ( 255, 0, 0 ) if last.is_open() else ( 0, 255, 0 ) pygame.draw.line( screen, colour, ( x - 2, y - 2 ), ( x + 2, y + 2 ), 1 ) pygame.draw.line( screen, colour, ( x + 2, y - 2 ), ( x - 2, y + 2 ), 1 ) x, y = pygame.mouse.get_pos() if self.has_grid: self.op( screen, self.front_grid, 1 ) x, y = self.snap( x ), self.snap( y ) if Mode.INSERT == mode: colour = ( 0, 0, 255 ) pygame.draw.line( screen, colour, ( x - 2, y - 2 ), ( x + 2, y + 2 ), 1 ) pygame.draw.line( screen, colour, ( x + 2, y - 2 ), ( x - 2, y + 2 ), 1 ) self.highlight( screen, polygons ) self.select( screen, polygons ) self.output_toolbar( screen, mode ) pygame.display.flip() def highlight( self, screen, polygons ): for polygon in polygons: all_points = polygon.get_points() highlights = [] for i in range( 0, len( all_points ) ): p = all_points[ i ] z = p.z - self.position.z if z < self.observer.z and p.z == self.position.z: this = Point( p.x - self.position.x, p.y - self.position.y, z ) highlights.append( [ this.rel_to( self.observer ) ] ) prev = all_points[ i - 1 ] if prev.z == self.position.z: last = Point( prev.x - self.position.x, prev.y - self.position.y, z ) highlights.append( [ last.rel_to( self.observer ), this.rel_to( self.observer ) ] ) for highlight in highlights: if len( highlight ) == 1: pygame.draw.circle( screen, self.highlight_colour, highlight[ 0 ], self.highlight_radius, 1 ) elif len( highlight ) == 2: pygame.draw.line( screen, self.highlight_colour, highlight[ 0 ], highlight[ 1 ], 1 ) def select( self, screen, polygons ): for polygon in polygons: all_points = polygon.get_points() highlights = [] for i in range( 0, len( all_points ) ): p = all_points[ i ] z = p.z - self.position.z if polygon.is_selected() and z < self.observer.z and p.z == self.position.z: this = Point( p.x - self.position.x, p.y - self.position.y, z ) prev = all_points[ i - 1 ] last = Point( prev.x - self.position.x, prev.y - self.position.y, z ) highlights.append( [ last.rel_to( self.observer ), this.rel_to( self.observer ) ] ) for highlight in highlights: pygame.draw.line( screen, ( 255, 0, 0 ), highlight[ 0 ], highlight[ 1 ], 1 ) def op( self, screen, polygons, w ): for polygon in polygons: points = [ ] for p in polygon.get_points(): points.append( ( p.x, p.y ) ) if len( points ) == 1: screen.set_at( points[ 0 ], polygon.get_colour() ) elif len( points ) == 2: pygame.draw.line( screen, polygon.get_colour(), points[ 0 ], points[ 1 ], 1 ) else: pygame.draw.polygon( screen, polygon.get_colour(), points, polygon.get_width() ) def output_polygons( self, screen, polygons, w ): for polygon in polygons: all_points = polygon.get_points() points = [] for i in range( 0, len( all_points ) ): p = all_points[ i ] z = p.z - self.position.z if z < self.observer.z: points.append( Point( p.x - self.position.x, p.y - self.position.y, z ).rel_to( self.observer ) ) else: points = [ ] break if len( points ) == 1: screen.set_at( points[ 0 ], polygon.get_colour() ) elif len( points ) == 2: pygame.draw.line( screen, polygon.get_colour(), points[ 0 ], points[ 1 ], 1 ) elif len( points ) > 2: pygame.draw.polygon( screen, polygon.get_colour(), points, polygon.get_width() ) def get_it_from( self, a, b ): return ( self.get_it( a.x, a.z, b.x, b.z ), self.get_it( a.y, a.z, b.x, b.z ) ) def get_it( self, x1, y1, x2, y2 ): m = self.slope( x1, y1, x2, y2 ) return ( m * x1 - y1 ) / 0.1 if m == 0 else m def slope( self, x1, y1, x2, y2 ): under = ( x2 - x1 ) return ( y2 - y1 ) / 0.1 if under == 0 else under def make_square( self, top_left, bottom_right, z ): colour = 255 - z % 100 a, b = top_left c, d = bottom_right square = Polygon( ( colour, colour, colour ) ).add( Point( a, b, z ) ) \ .add( Point( b, c, z ) ).add( Point( c, d, z ) ) \ .add( Point( d, a, z ) ) square.set_width( 1 ) return square def make_squares( self, n, top_left, bottom_right, separation ): squares = [] for i in range( 0, n ): squares.append( self.make_square( top_left, bottom_right, self.position.z - i * separation ) ) return squares def output_toolbar( self, screen, mode ): status = "" if Mode.INSERT == mode: status = "-- INSERT --" elif Mode.OBSERVE == mode: status = "-- OBSERVE --" elif Mode.VISUAL == mode: status = "-- VISUAL --" pygame.draw.rect( screen, ( 205, 205, 205 ), ( ( 0, 400 ), ( 400, 440 ) ), 0 ) text = self.font.render( status, True, self.font_colour ) screen.blit( text, ( 5, 405 ) ) text = self.font.render( str( self.observer ), True, self.font_colour ) screen.blit( text, ( 5, 425 ) ) text = self.font.render( str( self.position ), True, self.font_colour ) screen.blit( text, ( 125, 425 ) ) x, y = pygame.mouse.get_pos() if self.has_grid: x, y = self.snap( x ), self.snap( y ) text = self.font.render( str( ( x, y ) ), True, self.font_colour ) screen.blit( text, ( 245, 425 ) )
model = model.set_grid( False ) elif 'sg' == option: snapgrid = True model = model.set_grid( True ) elif 'nosg' == option: snapgrid = False elif Command.WRITE == command[ 0 ]: filename = command[ 1 ][ 0 ] if not filename.endswith( '.3d' ): filename += '.3d' write_objects( filename, objects ) print "Wrote '" + filename + "'" elif Command.QUIT == command[ 0 ]: running = False if Mode.OBSERVE == mode: if K_UP == event.key: observer = observer.move_up( 10 ) elif K_RIGHT == event.key: observer = observer.move_right( 10 ) elif K_DOWN == event.key: observer = observer.move_down( 10 ) elif K_LEFT == event.key: observer = observer.move_left( 10 ) elif K_j == event.key: observer = observer.move_forward( 10 ) elif K_k == event.key and observer.z > model.position.z: observer = observer.move_back( 10 ) model = model.update_observer( observer ) else: if K_UP == event.key: model = model.move_up( 10 ) elif K_RIGHT == event.key: model = model.move_right( 10 ) elif K_DOWN == event.key: model = model.move_down( 10 ) elif K_LEFT == event.key: model = model.move_left( 10 ) elif K_j == event.key and model.position.z < observer.z: model = model.move_forward( 10 ) elif K_k == event.key: model = model.move_back( 10 ) elif MOUSEBUTTONUP == event.type: if Mode.INSERT == mode: