Skip to content

Instantly share code, notes, and snippets.

@zeffii
Last active June 9, 2020 15:22
Show Gist options
  • Save zeffii/2c90a56c64d241ee03e3477ffb00bed0 to your computer and use it in GitHub Desktop.
Save zeffii/2c90a56c64d241ee03e3477ffb00bed0 to your computer and use it in GitHub Desktop.
how to use cffi
# BEGIN GPL LICENSE BLOCK #####
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# END GPL LICENSE BLOCK #####
import bpy
from bpy.props import StringProperty, IntVectorProperty, FloatVectorProperty, BoolProperty
from sverchok.utils.snlite_importhelper import (
set_autocolor
)
from sverchok.node_tree import SverchCustomTreeNode
from sverchok.data_structure import updateNode
FAIL_COLOR = (0.8, 0.1, 0.1)
READY_COLOR = (0, 0.8, 0.95)
class SvCFFINodeCallBack(bpy.types.Operator):
bl_idname = "node.cffi_ui_callback"
bl_label = "CFFI Node callback"
fn_name: bpy.props.StringProperty(default='')
def execute(self, context):
getattr(context.node, self.fn_name)()
return {'FINISHED'}
class SvCFFINode(bpy.types.Node, SverchCustomTreeNode):
''' cffi CFFI Node /// a cffi Node '''
bl_idname = 'SvCFFINode'
bl_label = 'CFFI Node'
bl_icon = 'SCRIPTPLUGINS'
script_name: StringProperty()
script_str: StringProperty()
n_id: StringProperty(default='')
def draw_label(self):
if self.script_name:
return 'CFFI: ' + self.script_name
else:
return self.bl_label
def sv_init(self, context):
self.use_custom_color = False
def clear_sockets(self):
self.inputs.clear()
self.outputs.clear()
def check_socket(self, dir, name, type):
if dir == "in":
if name not in self.inputs:
if type == "v":
self.inputs.new("SvVerticesSocket", name)
elif type == "m":
self.inputs.new("SvMatrixSocket", name)
elif type == "s":
self.inputs.new("SvStringsSocket", name)
elif dir == "out":
if name not in self.outputs:
if type == "v":
self.outputs.new("SvVerticesSocket", name)
elif type == "m":
self.outputs.new("SvMatrixSocket", name)
elif type == "s":
self.outputs.new("SvStringsSocket", name)
def make_sockets(self, mdl):
sock_str = ""
if hasattr(mdl, "sockets"):
sock_str = mdl.sockets
if sock_str != "":
for line in sock_str.split('\n'):
L = line.strip()
if L.startswith("'''"):
quotes += 1
if quotes == 2:
break
elif L.startswith('in ') or L.startswith('out '):
split = L.split(' ')
if len(split) != 3:
continue
socket_dir = split[0]
socket_name = split[1]
socket_type = split[2]
self.check_socket(socket_dir, socket_name, socket_type)
def load(self, name):
import imp
if name in bpy.data.texts:
self.script_str = bpy.data.texts[name].as_string()
mdl = imp.new_module(name)
#warning, dangerous !
exec(self.script_str, mdl.__dict__)
return mdl
else:
return None
def compile(self, mdl):
import cffi, os
py, build, srcs, incs, libs, lib_dirs, comp_arg, link_arg, lang = mdl.setup()
ffi = cffi.FFI()
ffi.cdef(mdl.cdef)
spaths = []
for s in mdl.sources:
spaths.append(os.path.join(srcs, s));
inc_dirs = [py, srcs]
inc_dirs.extend(incs)
#ffi.set_source(lib, code, sources=spaths, include_dirs=[py, src], language='c++')
#ffi.compile(verbose=True, tmpdir=build)
#verify automatically caches the binary, so it wont recompile over and over again
lib = ffi.verify(mdl.code,
tmpdir=build,
sources=spaths,
include_dirs=inc_dirs,
libraries=libs,
library_dirs=lib_dirs,
extra_compile_args=comp_arg,
extra_link_args=link_arg,
language=lang) # to be able to compile c++ too
return lib, ffi
def execute(self):
name = self.script_name
mdl = self.load(name)
if mdl is not None:
lib, ffi = self.compile(mdl)
#create sockets if not existing
self.make_sockets(mdl)
# execute glue / bind code from c script
# pass current node there, so we can access the socket variables
mdl.execute(lib, ffi, self)
#for experimental reason
#print("SHIT3")
return lib, ffi, mdl
def clear(self):
self.script_str = ''
self.script_name = ''
self.clear_sockets()
def process(self):
#try:
self.execute()
set_autocolor(self, True, READY_COLOR)
#except:
# set_autocolor(self, True, FAIL_COLOR)
def draw_buttons(self, context, layout):
sn_callback = 'node.cffi_ui_callback'
col = layout.column(align=True)
row = col.row()
if not self.script_str:
row.prop_search(self, 'script_name', bpy.data, 'texts', text='', icon='TEXT')
row.operator(sn_callback, text='', icon='PLUGIN').fn_name = 'process'
else:
row.operator(sn_callback, text='Reload').fn_name = 'process'
row.operator(sn_callback, text='Clear').fn_name = 'clear'
#self.custom_draw(context, layout)
classes = [
SvCFFINodeCallBack,
SvCFFINode
]
def register():
_ = [bpy.utils.register_class(name) for name in classes]
def unregister():
_ = [bpy.utils.unregister_class(name) for name in classes]
try:
unregister()
except:
pass
register()
print("x")
sockets = """
in points v
in edges s
out new_verts v
"""
cdef = """
void intersectAll(float vertices[][3], int lines[][2], int count, float new_verts[][3], int num_new_verts[]);
"""
import bpy
code = bpy.data.texts['xall.c'].as_string()
sources = [ "mathlib.c" ]
def setup():
import os, bpy
from pathlib import Path
root = Path(bpy.data.filepath).parent
py = Path(bpy.app.binary_path).parent / "2.80" / "python" / "include";
build = Path(root) / "build";
srcroot = Path(root) / "mathlib"
incroot = Path(root) / "mathlib"
srcs = str(srcroot)
incs = [str(incroot)]
libs = []
lib_dirs = []
comp = []
link = []
lang = 'c'
return str(py), str(build), srcs, incs, libs, lib_dirs, comp, link, lang
def execute(lib, ffi, node):
points = node.inputs["points"].sv_get(default=[[]])[0]
edges = node.inputs["edges"].sv_get(default=[[]])[0]
if not points and edges:
return
new_verts = xall(lib, ffi, points, edges)
if new_verts:
node.outputs["new_verts"].sv_set(new_verts)
def xall(lib, ffi, points, edges):
if not points and edges:
return [[]]
n = len(edges)
max_num = int(((n*n) - n) / 2)
new_verts = ffi.new("float[][3]", max_num)
cpoints = ffi.new("float[][3]", len(points))
cedges = ffi.new("int[][2]", len(edges))
num_new_verts = ffi.new('int[]', 1)
for i, p in enumerate(points):
cpoints[i] = p
for i, e in enumerate(edges):
cedges[i][0] = e[0]
cedges[i][1] = e[1]
count = len(edges)
lib.intersectAll(cpoints, cedges, count, new_verts, num_new_verts)
verts = []
for i in range(max_num):
v = new_verts[i]
verts.append((v[0], v[1], v[2]))
return [verts]
// # xall.c
#include "mathlib.h"
void intersectAll(float vertices[][3], int lines[][2], int count, float new_verts[][3], int num_new_verts[]){
int isect_result;
// int count = sizeof(lines) / sizeof(lines[0]);
printf("%d", count);
const float epsilon = 0.0001f;
float v1[3], v2[3], v3[3], v4[3], r_i1[3], r_i2[3];
int intersections = 0;
for (int i = 0; i <= count; i++) {
for (int j = 0; j <= count; j++) {
// # skip identical indices and 1 half of 2d matrix by ignoring duplicate comparrisons
if (j <= i){ continue; }
if (lines[i][0] == lines[j][0] || lines[i][0] == lines[j][1]) { continue; }
if (lines[i][1] == lines[j][0] || lines[i][1] == lines[j][1]) { continue; }
copy_v3_v3(v1, vertices[lines[i][0]]);
copy_v3_v3(v2, vertices[lines[i][1]]);
copy_v3_v3(v3, vertices[lines[j][0]]);
copy_v3_v3(v4, vertices[lines[j][1]]);
isect_result = isect_line_line_epsilon_v3(v1, v2, v3, v4, r_i1, r_i2, epsilon);
if (isect_result == 0) { continue; }
else if (isect_result == 1) { copy_v3_v3(new_verts[intersections], r_i1); }
else if (isect_result == 2) {
copy_v3_v3(new_verts[intersections], r_i1);
copy_v3_v3(new_verts[intersections+1], r_i1);
}
intersections += isect_result;
}
}
// num_new_verts[0] = intersections;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment