4
$\begingroup$

I want to draw script-calculated curves with the grease pencil. Using this line of python I'm able to redefine the points of an existing stroke, shown below.

So far I can't add a point like this:

newpt = points[200].copy()  # error
points.append(newpt)        # error

or this:

points.items().append('put various things here')   # NO error, but no new points either

where:

points = bpy.data.grease_pencil[0].layers[0].active_frame.strokes[0].points

So instead, I draw a big scribble, find out the length using the interactive python window (in this case it's 427) and then make a new set of coordinates of the same length.

How can I create or at least increase the number of points of a grease pencil stroke? I'm looking for something similar to the way I can make new mesh objects:

# example: make a new mesh object - I wish I could make grease pencil objects like this!!!
newmesh = bpy.data.meshes.new('Newmesh')
newobj  = bpy.data.objects.new('Newobj', newmesh)

bpy.context.scene.objects.link(newmesh)
newmesh.from_pydata(verts, [], faces)

krazy koil

import numpy as np
import bpy

pi, twopi = np.pi, 2*np.pi

theta = np.linspace(0, 20*twopi, 427)
theta -= theta.mean()

r = 4 - 2*np.cos(0.1*theta)

y = theta / twopi
x = r*np.cos(theta)
z = r*np.sin(theta)

krazy_koil_points = np.vstack((x, y, z)).T

points = bpy.data.grease_pencil[0].layers[0].active_frame.strokes[0].points
for i, point in enumerate(points):
    point.co = krazy_koil_points[i]

Same thing, but without numpy:

import bpy, math

pi, twopi = math.pi, 2*math.pi

theta = [20*twopi * i / 427. for i in range(427)]

mean  = sum(theta)/float(len(theta))

theta = [th - mean for th in theta]

r = [4 - 2*math.cos(0.1*th) for th in theta]

y = [th/twopi for th in theta]
x = [a*math.cos(b) for a, b in zip(r, theta)]
z = [a*math.sin(b) for a, b in zip(r, theta)]

krazy_koil_points = list(zip(x, y, z))

points = bpy.data.grease_pencil[0].layers[0].active_frame.strokes[0].points
for i, point in enumerate(points):
    point.co = krazy_koil_points[i]
$\endgroup$

1 Answer 1

8
$\begingroup$

What you're looking for is the add method within the points collection.

Fixed the script to work with fresh clean files that have no grease pencil data.

enter image description here

import bpy, math

S  = bpy.context.scene

# Create grease pencil data if none exists
if not S.grease_pencil:
    a = [ a for a in bpy.context.screen.areas if a.type == 'VIEW_3D' ][0]
    override = {
        'scene'         : S,
        'screen'        : bpy.context.screen,
        'object'        : bpy.context.object,
        'area'          : a,
        'region'        : a.regions[0],
        'window'        : bpy.context.window,
        'active_object' : bpy.context.object
    }

    bpy.ops.gpencil.data_add( override )

gp = S.grease_pencil

# Reference grease pencil layer or create one of none exists
if gp.layers:
    gpl = gp.layers[0]
else:
    gpl = gp.layers.new('gpl', set_active = True )

# Reference active GP frame or create one of none exists    
if gpl.frames:
    fr = gpl.active_frame
else:
    fr = gpl.frames.new(1) 

# Create a new stroke
str = fr.strokes.new()
str.draw_mode = '3DSPACE'

# Number of stroke points
strokeLength = 500 

# Add points
str.points.add(count = strokeLength )

pi, twopi = math.pi, 2*math.pi

theta = [20 * twopi * i / strokeLength for i in range(strokeLength)]

mean  = sum(theta)/float(len(theta))

theta = [th - mean for th in theta]

r = [4 - 2*math.cos(0.1*th) for th in theta]

y = [th/twopi for th in theta]
x = [a*math.cos(b) for a, b in zip(r, theta)]
z = [a*math.sin(b) for a, b in zip(r, theta)]

krazy_koil_points = list(zip(x, y, z))

points = str.points
for i, point in enumerate(points):
    points[i].co = krazy_koil_points[i]

For blender 3.2

import bpy, math

bpy.ops.object.gpencil_add(type='EMPTY')

gp_name = bpy.context.object.name

gp = bpy.data.grease_pencils[gp_name]


# Reference grease pencil layer or create one of none exists
if gp.layers:
    gpl = gp.layers[0]
else:
    gpl = gp.layers.new('gpl', set_active = True )

# Reference active GP frame or create one of none exists    
if gpl.frames:
    fr = gpl.active_frame
else:
    fr = gpl.frames.new(1) 

# Create a new stroke
str = fr.strokes.new()
str.display_mode = '3DSPACE'

# Number of stroke points
strokeLength = 500 

# Add points
str.points.add(count = strokeLength )

pi, twopi = math.pi, 2*math.pi

theta = [20 * twopi * i / strokeLength for i in range(strokeLength)]

mean  = sum(theta)/float(len(theta))

theta = [th - mean for th in theta]

r = [4 - 2*math.cos(0.1*th) for th in theta]

y = [th/twopi for th in theta]
x = [a*math.cos(b) for a, b in zip(r, theta)]
z = [a*math.sin(b) for a, b in zip(r, theta)]

krazy_koil_points = list(zip(x, y, z))

points = str.points
for i, point in enumerate(points):
    points[i].co = krazy_koil_points[i]

enter image description here

$\endgroup$
6
  • $\begingroup$ Thanks :) Weird though, I use 2.74 here as well and it did work. Curious to see the errors you ran into. $\endgroup$
    – TLousky
    Commented Mar 17, 2016 at 15:00
  • $\begingroup$ Found the issue, fixing the script in a min (no grease pencil data exists, you can fix it manually by creating adding a grease pencil layer, but I'll check for it in the script and add it there too). $\endgroup$
    – TLousky
    Commented Mar 17, 2016 at 15:03
  • $\begingroup$ @uhoh, fixed it, please have another go. $\endgroup$
    – TLousky
    Commented Mar 17, 2016 at 15:13
  • 1
    $\begingroup$ Hey super! This is exactly what I need! I like your answers - they are always concise and yet complete with running script. Thanks for your time and persistence :) $\endgroup$
    – uhoh
    Commented Mar 17, 2016 at 15:36
  • 1
    $\begingroup$ oh, I was so happy to see this work that I forgot to accept the answer @@ thanks! $\endgroup$
    – uhoh
    Commented Mar 17, 2016 at 16:27

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .