1. NodeBox 1
    1. Homepage
    2. NodeBox 3Node-based app for generative design and data visualization
    3. NodeBox OpenGLHardware-accelerated cross-platform graphics library
    4. NodeBox 1Generate 2D visuals using Python code (Mac OS X only)
  2. Gallery
  3. Documentation
  4. Forum
  5. Blog

Amending the BitBop.py Script

Posted by Viki on Jul 16, 2007



Dear NodeBoxers,

This Project has really inspired me and I would like to incorporate Nodebox into my illustration work but I'm really stuck on one particular script (BitBop.py)
I've played about with throttle in the to alter the composition, spaces between points, distribution of points, etc.

1)
In the same way that the text path is populated with primitives, I would really like to be able to import a vector path that I have designed in Illustrator and then populate it in the same way.
2)
Another thing puzzling me is how to change the primitive from a rectangular box to another type of primitive. I've created primitives from scratch in a new doc. but not sure how to amend this script. If I could import more artwork or images and use these to replace the primitive it would be a massive break through for me.
3)
Lastly, If for example I was to import artwork (varying shapes and scales) to use instead of primitives to populate the path how could I stop the shapes from touching or overlapping.


Any help would be hugely appreciated.
Thanks again for the good work you have done so far.
Viki x


Here is the Script as I currently have it:


size(700, 200)
# BitBop -- a fun demonstration of path.contains.
#
# The textpath command returns a BezierPath of the text that can
# be manipulated or, as demonstrated here, queried using path.contains.
# A grid is generated and everywhere a point in the path is encountered,
# a random square is drawn.

# Set the font and create the text path.
font("Verdana", 150)
tp = textpath("NodeBox", 10, 150)
#tp.draw() # Draws the underlying path

# Here are the variables that influence the composition:
ovsx = 200 # The horizontal resolution
ovsy = 100 # The vertical resolution
ox = 0 # The horizontal randomness each point has
oy = 1 # The vertical randomness each point has
dotsize = 5 # The maximum size of one dot.
dx = WIDTH / float(ovsx) +2 # The width each dot covers
dy = HEIGHT / float(ovsy) +2 # The height each dot covers

# We create a grid of the specified resolution.
# Each x,y coordinate is a measuring point where
# we check if it falls within the path.
for x, y in grid(ovsx, ovsy):
sz=random(dotsize)
# Create the point that will be checked
px = x*dx-sz
py = y*dy-sz
# Only do something if the point falls within the path bounds.
# You could add an "else" statement, that draws something in the
# empty positions.
if tp.contains(px, py):
# Change the color for each point -- try it out!
fill(random(), 0, 0, random())
rect(px+random(-ox, ox),
py+random(-oy, oy),
sz, sz)


 
Posted by tom on Jul 20, 2007

Hi Viki,

1) You can import SVG as paths with the SVG library. Download the library, save your Illustrator artwork as SVG files, and then you can do things in NodeBox like:

svg = ximport("svg")
data = open("artwork.svg").read()
paths = svg.parse(data) #a list of all the paths in the SVG file.
drawpath(paths[0])
A version of the bitbop code with SVG would look like this:
size(500, 500) 
 
ovsx = 200
ovsy = 100
ox = 0
oy = 1
dotsize = 5
dx = WIDTH / float(ovsx) +2
dy = HEIGHT / float(ovsy) +2
 
# SVG path instead of textpath
svg = ximport("svg")
data = open("path.svg").read()
paths = svg.parse(data)
flower = paths[0]
 
for x, y in grid(ovsx, ovsy): 
    sz=random(dotsize) 
    px = x*dx-sz 
    py = y*dy-sz 
    if flower.contains(px, py): # flower.contains instead of tp.contains 
        fill(random(), 0, 0, random()) 
        rect(px+random(-ox, ox), py+random(-oy, oy), sz, sz)
2) The rect() command draws a rectangle. So where it says rect in the script, a rectangle is being drawn. If you replace that line with something else like oval() or drawpath() you're going to see different shapes.

For example, try replacing the line with:
arrow(px+random(-ox, ox), py+random(-oy, oy), sz*4)
3) In general, determining if geometric shapes overlap is very hard. As to the code in bitbop, notice the line:
sz=random(dotsize)
The sz variable which is used to position and scale the rectangles is assigned a random value, causing bigger and smaller rectangles to overlap.

Try changing it to:
sz=dotsize
to get a much tighter grid.

Hope that helps!



Posted by Viki on Jul 24, 2007

This is great. Thanks so much for the tips. I've managed to import the SVG path and populate it with primitives.

I'm still a bit stuck on the effect i'm looking for which is to populate a SVG shape with other SVG shapes that I import. It would be really cool if i could populate the main SVG shape with randomly selected SVG files from the same directory.

x



Posted by Tom De Smedt on Jul 26, 2007

The choice() command picks a random element from a list. The files() command returns a list of files in a folder. So those two are easily combined in a new command that returns random SVG paths:

def randomsvg():    
    f = files("paths/*.svg")
    data = open(choice(f)).read()
    paths = svg.parse(data)
    return paths[0]
Then, instead of drawing rect()s inside the main SVG path you would do something like:
path = randomsvg()
# You would create each vector path in Illustrator 
# at the same position, and then move it around in NodeBox 
# with the translate command.
translate(px, py)
drawpath(path)



Posted by Viki on Jul 27, 2007

## I'm getting an error back from the following script: NodeBox/Examples/Path/Test2.21_.py", line 33, in ?
TypeError: 'BezierPath' object is not callable
###


size(300, 300)

ovsx = 50
ovsy = 50
ox = 0
oy = 1
dotsize = 2
dx = WIDTH / float(ovsx) +2
dy = HEIGHT / float(ovsy) +2

# SVG path instead of textpath
svg = ximport("svg")
data = open("path.svg").read()
paths = svg.parse(data)
flower = paths[0]

########
def randomsvg():
f = files("paths/*.svg")
data = open(choice(f)).read()
paths = svg.parse(data)
return paths[0]

path = randomsvg()
########

for x, y in grid(ovsx, ovsy):
sz=dotsize
px = x*dx-sz
py = y*dy-sz
if flower.contains(px, py): # flower.contains instead of tp.contains
fill(random(), -5, 2, random())
path(px+random(-ox, ox), py+random(-oy, oy), sz*4)



Posted by nex on Aug 09, 2007

This must be long fixed now, but in case anyone else is wondering: path would have to be drawn with drawpath, it is not a method or function or anything else you could call.