Nebula
Lots and lots of copies of vector paths in NodeBox.


NodeBox source code:
size(700,700) def angle(x0, y0, x1, y1): from math import degrees, atan2 a = degrees( atan2(y1-y0, x1-x0) ) return a def distance(x0, y0, x1, y1): from math import sqrt, pow return sqrt(pow(x1-x0, 2) + pow(y1-y0, 2)) def coordinates(x0, y0, distance, angle): from math import radians, sin, cos x1 = x0 + cos(radians(angle)) * distance y1 = y0 + sin(radians(angle)) * distance return x1, y1 def reflect(x0, y0, x1, y1, d=1.0, a=180): d *= distance(x0, y0, x1, y1) a += angle(x0, y0, x1, y1) x, y = coordinates(x0, y0, d, a) return x, y def merge(points, x, y, vx, vy, reflected=False, d=0): """ Smooth curves from points to x, y. """ beginpath(0,0) for pt in points: if not reflected: vx0, vy0 = pt.ctrl1.x, pt.ctrl1.y else: vx0, vy0 = reflect(pt.x, pt.y, pt.ctrl1.x, pt.ctrl1.y) moveto(pt.x, pt.y) curveto(vx0, vy0, vx, vy, x+random(-d,d), y+random(-d,d)) return endpath(draw=False) def points(n, vx, vy, h=(0.0,1.0), v=(0.0,1.0)): """ Random points in relative h,v box with vx, vy handles. """ points = [] for i in range(n): pt = PathElement() pt.x = WIDTH*h[0] + random(WIDTH*(h[1]-h[0])) pt.y = HEIGHT*v[0] + random(HEIGHT*(v[1]+v[0])) pt.ctrl1.x = pt.x+vx pt.ctrl1.y = pt.y+vy points.append(pt) return points def nebula(clr, bg=True, n=100, d=300, angle=0.5, iterations=100, tonality=0.1, growth=[1.01, 1.01, 0.98]): def _wrap_around(n, base=1): if n < 0: return base-n if n > base: return n-base return n def _draw_transformed(path, dx=0, dy=0, angle=0, scaling=1.0): rotate(angle) drawpath(BezierPath(path.path)) scale(scaling) translate(dx, dy) colormode(HSB) if bg: background( clr.hue, clr.saturation, max(0.15, clr.brightness*0.15) ) strokewidth(0.1) nofill() autoclosepath(False) # Create a number of points in a portion of the canvas. # The points all have the same handle vector. # Then, draw a line from each point to a focus point. h = (random(1-random(0.2)), random(0.2)) v = (random(1-random(0.2)), random(0.2)) pts = points(n, random(-d,d), random(-d,d), h, v) x, y = random(WIDTH), random(HEIGHT) vx, vy = x+random(-d/2,d*2), y+random(-d/4,d/4) path = merge(pts, x, y, vx, vy, d=random(d/2)) # Draw rotating versions of this path # that increment in size and horizontal position. # Colors are dark with variations in hue. #transform(CORNER) direction = choice((-1,1)) for i in range(iterations/2): h = _wrap_around((clr.hue + random(tonality)*direction)) s = clr.saturation b = clr.brightness * (0.4 + random(0.6)) stroke(h, s, b, random(0.25)) _draw_transformed(path, 1.5, 0, angle, growth[0]) for i in range(choice((2,3))): reset() # Draw lines from the foucs point to new points, # with their handles reflected from the focus' handles. h = (random(1-random(0.2)), random(0.2)) v = (random(1-random(0.2)), random(0.2)) pts = points(n, random(-d,d), random(-d,d), h, v) vx, vy = reflect(x, y, vx, vy) path = merge(pts, x, y, vx, vy, reflected=True, d=10) # Incremental variations of the path. # Colors are light and desaturated. for i in range(iterations/2): h = clr.hue s = clr.saturation + random(-0.6) b = random(0.2) + 0.8 stroke(h, s, b, random(0.25)) _draw_transformed(path, random(), random(), angle, growth[1]) # Variations that decrement in size. # Colors are light with variations in hue. direction = choice((-1,1)) for i in range(iterations): h = _wrap_around((clr.hue + random(tonality)*direction)) s = clr.saturation + random(-0.2) b = random(0.2) + 0.8 stroke(h, s, b, random(0.25)) _draw_transformed(path, 1.5, 0, angle, growth[2]) reset() colors = ximport("colors") colors.shadow(alpha=0.02, dx=30, dy=30) colormode(RGB) clr = color(random(), random(), random()) nebula(clr)

