A tool/toy that generates a random set of points in 3D space and draws links between them, with colour depending on direction and brightness depending on proximity. Can be rotated in 3D. Wrote this for kicks in an Algorithms & Analysis class after the lecturer started talking about graph link weights. Requires PyGame. Runs in Python 2.7 or 3.
Source: closestpoints.py
(Requires vec2.py)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 |
#! python3 # Random 3D point distance visualisation toy # By Oscar Francis # Started Tues 20 Aug 2013 using Python 2.7.4 from math import * import sys import random from vec2 import * import pygame, pygame.event, pygame.draw, string from pygame.locals import * pygame.init() width = 640 height = 640 wh = int(width / 2) hh = int(height / 2) clock = pygame.time.Clock() window = pygame.display.set_mode( ( width, height ) ) rot = 0.0 tip = 0.0 rotBase = 0.0 tipBase = 0.0 lw = 2 darken = 20 col = 1.0 grey = 0.5 cutFactor = 0.15 numPoints = 300 numLinks = (int)((numPoints**2)/2) targ = Vec2( 0, 0 ) targBase = Vec2( 0, 0 ) tracking = False maxLength = Vec3( 0, 0, 0 ).vecTo( Vec3( width, height, width ) ).mag() cutoff = maxLength * cutFactor def get_key(): event = pygame.event.poll() if event.type == KEYDOWN: return event.key class Point( Vec3 ): def __init__( self ): self.x = random.randrange( width ) self.y = random.randrange( height ) self.z = random.randrange( width ) self.drawPoint = ( 0, 0 ) def updateDrawPoint( self ): xMod, yMod, zMod = self.x - wh, self.y - hh, self.z - wh r = sqrt( xMod**2 + zMod**2 ) theta = atan2( zMod, xMod ) - rot xMod,zMod = r * cos( theta ), r * sin( theta ) yMod = sqrt( zMod**2 + yMod**2 ) * sin( atan2( yMod, zMod ) + tip ) self.drawPoint = ( int(xMod) + wh, int(yMod) + hh ) class Link( object ): def __init__( self, startPoint, endPoint ): self.start = startPoint self.end = endPoint vec = startPoint.vecTo( endPoint ) self.length = vec.mag() c = ( (maxLength - self.length) / maxLength )**darken * 255 self.col = tuple( c*grey + (abs( coord ) / self.length)*c*(1 - grey) for coord in vec ) def getLength( self ): return self.length points = tuple( Point() for x in range( numPoints ) ) links = [None] * numLinks x = 0 for i in range( numPoints - 1 ): for j in range( i+1, numPoints ): links[x] = Link( points[i], points[j] ) x += 1 links = links[:x] # prune unused elements links.sort( key=Link.getLength, reverse=True ) # sort into correct draw order links = tuple( links ) def drawScreen(): pygame.draw.rect( window, (0, 0, 0), pygame.Rect( 0, 0, width, height ), 0 ) for p in points: p.updateDrawPoint() for l in links: if l.length < cutoff: pygame.draw.line( window, l.col, l.start.drawPoint, l.end.drawPoint, lw ) for p in points: pygame.draw.circle( window, (255, 255, 255), p.drawPoint, 1, 0 ) if tracking: pygame.draw.line( window, (255, 255, 255), (targBase.x,targBase.y), (targ.x,targ.y), 2 ) numDraws = 0 pygame.display.flip() drawScreen() while True: for event in pygame.event.get(): if event.type == pygame.QUIT: sys.exit(0) clock.tick() inkey = get_key() if inkey == K_ESCAPE: sys.exit(0) # update target if pygame.mouse.get_pressed()[0]: if not tracking: targBase.x, targBase.y = pygame.mouse.get_pos() tracking = True rotBase = rot tipBase = tip targ.x, targ.y = pygame.mouse.get_pos() drag = targBase.vecTo( targ ) rot = (drag.x / width) * 4 + rotBase tip = (drag.y / height) * 4 + tipBase drawScreen() else: if tracking: tracking = False drawScreen() |