4

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]
uhoh
  • 2,667
  • 2
  • 27
  • 56

1 Answers1

8

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 - 2math.cos(0.1th) 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 - 2math.cos(0.1th) 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

Duarte Farrajota Ramos
  • 59,425
  • 39
  • 130
  • 187
TLousky
  • 16,043
  • 1
  • 40
  • 72
  • Thanks :) Weird though, I use 2.74 here as well and it did work. Curious to see the errors you ran into. – TLousky Mar 17 '16 at 15:00
  • 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). – TLousky Mar 17 '16 at 15:03
  • @uhoh, fixed it, please have another go. – TLousky Mar 17 '16 at 15:13
  • 1
    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 :) – uhoh Mar 17 '16 at 15:36
  • 1
    oh, I was so happy to see this work that I forgot to accept the answer @@ thanks! – uhoh Mar 17 '16 at 16:27
  • Used as ref for answer here Appears Ok without the overridden op and adding new gp via bpy.data.grease_pencil – batFINGER Sep 29 '18 at 11:07