Coder Social home page Coder Social logo

linusnie / blender-plots Goto Github PK

View Code? Open in Web Editor NEW
119.0 2.0 4.0 30.9 MB

Python library for making 3D plots with blender

License: GNU General Public License v3.0

Python 100.00%
3d blender blender-addon data-viz geometry-nodes plotting visualization

blender-plots's Introduction

blender-plots

Ever wonder if you could use Blender for plotting, like you would with Matplotlib? With blender-plots you can!

out_15fps_v3_loop image info

Blender can indeed be a great tool for visualization, but if you're starting out with numpy arrays getting even a simple plot with colors requires a lot of digging through the python API and wrangling node trees. The goal of this library/addon is to automate all of that and make the visualization process as painless as possible for someone familiar with matplotlib-like libraries.

At the moment this readme acts as the documentation. The easiest way to get started is to have a look at the examples below and in examples.ipynb. Feel free to open an issue if anything is unclear or doesn't work as intended, feedback and suggestions are appreciated!

Installation

Option 1: Install with pip (recommended)

Run the following command in a terminal window:

[path_to_blender]/[version_number]/python/bin/python3.10 -m pip install blender_plots

This will download the library to [path_to_blender]/[version_number]/python/lib/python3.10/site-packages/blender-plots.

If you're using blender_notebook as described below you can instead pip install to the virtual environment to keep the Blender python environment clean. However with this method the library won't be available when lauching Blender without the notebook.

Option 2: Install as addon

  • Go to Code > Download ZIP above and then in blender go to Edit > Preferences > Add-ons > install and select the downloaded file (or simply git-clone this repo to the blender addons folder).
  • Go to the addons panel in blender, search for blender plots and click on the tick-box.
  • You should now be able to run import blender_plots as bplt in the python console.

Set up blender notebook (optional)

Since the built-in code editor isn't great I recommend using jupyter notebooks with a blender kernel for script heavy use cases.

In a virtual environment, run:

python -m pip install blender_notebook ipykernel
blender_notebook install --blender-exec [path_to_blender]/blender --kernel-name blender

You should then be able to select blender as kernel in your preferred notebook editor.

Examples

Scatterplots

Scatterplots can be created with bplt.Scatter which expects three arrays x, y, z with the same length N containing coordinates to plot (or equivalently a single Nx3 array as the first argument). Color can also be set using the color= argument, which expects aNx3 or Nx4 numpy array with RGB or RGBA color values. Passing in a single RGB or RGBA value sets the same color for all points.

If the output is all gray it's probably because the rendering mode is set to Solid. In that case make sure to change it to Material Preview or Rendered by clicking on one of the sphere icons in the top right corner of the 3D view.

import numpy as np
import blender_plots as bplt
n, l = 150, 100
x, y = np.meshgrid(np.linspace(0, l, n), np.linspace(0, l, n))
x, y = x.ravel(), y.ravel()

z = np.sin(2*np.pi * x / l)*np.sin(2*np.pi * y / l) * 20
bplt.Scatter(x, y, z, color=(1, 0, 0), name="red")

z = np.sin(4*np.pi * x / l)*np.sin(4*np.pi * y / l) * 20 + 40
bplt.Scatter(x, y, z, color=(0, 0, 1), name="blue")

image info

Surface plots

Surface plots can be created in the same way, except using MxNx3 arrays for x, y, z. Faces are then added between points neighbouring along the x and y axes. Colors and animation can be added in the same way as with scatterplots.

import numpy as np
import blender_plots as bplt
n, l = 150, 100
x, y = np.meshgrid(np.linspace(0, l, n), np.linspace(0, l, n))

z = np.sin(2*np.pi * x / l)*np.sin(2*np.pi * y / l) * 20
bplt.Surface(x, y, z, color=(1, 0, 0), name="red")

z = np.sin(4*np.pi * x / l)*np.sin(4*np.pi * y / l) * 20 + 40
bplt.Surface(x, y, z, color=(0, 0, 1), name="blue")

image info

Arrow plots

Create an arrow plot by providing an Nx3 array of starting points and an Nx3 array of vectors representing the arrows.

import numpy as np
import blender_plots as bplt
import bpy

n, a, I = 25, 50, 100

bpy.ops.mesh.primitive_torus_add(major_radius=a, minor_radius=a / 100)
phis = np.linspace(0, 2 * np.pi, n)
thetas = np.linspace(0, 2 * np.pi, 1000)

def integrate_B(point):
    return I * a * (np.array([
        point[2] * np.cos(thetas),
        -point[2] * np.sin(thetas),
        -point[0] * np.cos(thetas) - point[1] * np.sin(thetas) + a
    ]) / np.linalg.norm(np.array([
        point[0] - a * np.cos(thetas),
        point[1] - a * np.sin(thetas),
        point[2] + np.zeros_like(thetas),
    ]), axis=0) ** 3).sum(axis=1) * (thetas[1] - thetas[0])

phis = np.linspace(0, 2 * np.pi, n)
thetas = np.linspace(0, 2 * np.pi, 100)

zeros = np.zeros(n)
for r in [a / 2, 3 * a / 4, a]:
    x, y, z = (a +  r * np.cos(phis)), zeros, r * np.sin(phis)
    points = np.array([x, y, z]).T
    B = np.apply_along_axis(integrate_B, 1, points)
    bplt.Arrows(points, B, color=(1, 0, 0), name=f"B_{r}", radius=.3, radius_ratio=3)

phis = np.linspace(0, 2 * np.pi, 13)
x, y, z = 1.01 * a * np.cos(phis), 1.01 * a * np.sin(phis), np.zeros(phis.shape)
points = np.array([x, y, z]).T
current_directions = np.array([-y, x, z]).T
bplt.Arrows(points, current_directions * .5, color=(0, 0, 1), name=f"I_directions", radius=.3, radius_ratio=3)
bplt.Scatter(points, color=(0, 0, 1), name=f"I_points", marker_type='ico_spheres', radius=1, subdivisions=3)

image info

Animations

To get an animated plot, just pass in x, y, z as TxN arrays instead (or TxNx3 as the first argument):

import numpy as np
import blender_plots as bplt
n, l, T = 150, 100, 100
t, x, y = np.meshgrid(np.arange(0, T), np.linspace(0, l, n), np.linspace(0, l, n), indexing='ij')
t, x, y = t.reshape((T, -1)), x.reshape((T, -1)), y.reshape((T, -1))

z = np.sin(2*np.pi * x / l) * np.sin(2*np.pi * y / l) * np.sin(2*np.pi * t / T) * 20
bplt.Scatter(x, y, z, color=(1, 0, 0), name="red")

z = np.sin(4*np.pi * x / l) * np.sin(4*np.pi * y / l) * np.sin(8*np.pi * t / T) * 20 + 40
bplt.Scatter(x, y, z, color=(0, 0, 1), name="blue")
sinusoids_animated.mp4

For animated surface plots the input shape should be TxMxNx3.

Visualizing point clouds

Since all heavy operations are done through numpy arrays or blender nodes it's possible to visualize large point clouds with minimal overhead. For example, Here is one with 1M points:

import numpy as np
import blender_plots as bplt
points = np.loadtxt("/home/linus/Downloads/tikal-guatemala-point-cloud/source/fovea_tikal_guatemala_pcloud.asc")
scatter = bplt.Scatter(points[:, :3] - points[0, :3], color=points[:, 3:]/255, size=(0.3,0.3,0.3))

image info

You can find the model here (select .asc format). Original source: OpenHeritage, license: CC Attribution-NonCommercial-ShareAlikeCC.

Marker options

You can swap from the cube to any other mesh primitive using the marker_type argument. In blender 3.1 the options are cones, cubes, cylinders, grids, ico_spheres, circles, lines or uv_spheres.

image info

Each marker type can further be configured by passing in node settings as parameter arguments. For example from the cone node docs we can see that it has the parameters Radius Top and Radius Bottom, these can be set directly by passing radius_top=... and radius_bottom=... to bplt.Scatter:

import numpy as np
import blender_plots as bplt
n = int(1e2)
scatter = bplt.Scatter(
    np.random.rand(n, 3)*50,
    color=np.random.rand(n, 3),
    marker_type="cones",
    radius_bottom=1,
    radius_top=3,
    randomize_rotation=True
)

image info

Similarly, the cube node has a vector-valued Size parameter:

import numpy as np
import blender_plots as bplt
n = int(1e2)
bplt.Scatter(
    np.random.rand(n, 3)*50,
    color=np.random.rand(n, 3),
    size=(5, 1, 1),
    randomize_rotation=True
)

image info

This is achieved by automatically converting input arguments to geometry node properties. See blender_utils.py for more details.

Rotating markers

Each marker can be assigned a rotation using the argument marker_rotation=..., similarly to the color argument it supports passing a single value for all points, one for each point, or one for each point and timestamp. The supported formats are XYZ euler angles in radians (by passing an Nx3 or NxTx3 array) or rotation matrices (by passing a Nx3x3 or NxTx3x3 array). As shown in the previous examples passing randomize_rotation=True assigns a random rotation to each marker.

import numpy as np
import blender_plots as bplt
def get_rotaitons_facing_point(origin, points):
    n_points = len(points)
    d = (origin - points) / np.linalg.norm(origin - points, axis=-1)[:, None]
    R = np.zeros((n_points, 3, 3))
    R[..., -1] = d
    R[..., 0] = np.cross(d, np.random.randn(n_points, 3))
    R[..., 0] /= np.linalg.norm(R[..., 0], axis=-1)[..., None]
    R[..., 1] = np.cross(R[..., 2], R[..., 0])
    return R

n = 5000
points = np.random.randn(n, 3) * 20
rots = get_rotaitons_facing_point(np.zeros(3), points)
s = bplt.Scatter(
    points,
    marker_rotation=rots,
    color=np.array([[1.0, 0.1094, 0.0], [0.0, 0.1301, 1.0]])[np.random.randint(2, size=n)],
    size=(1, 1, 5),
)

image info

Custom mesh as marker

You can also use an existing mesh by passing it to marker_type=...:

import numpy as np
from colorsys import hls_to_rgb
import bpy
import blender_plots as bplt
bpy.ops.mesh.primitive_monkey_add()
monkey = bpy.context.active_object
monkey.hide_viewport = True
monkey.hide_render = True
n = int(.5e2)

scatter = bplt.Scatter(
    50 * np.cos(np.linspace(0, 1, n)*np.pi*4),
    50 * np.sin(np.linspace(0, 1, n)*np.pi*4),
    50 * np.linspace(0, 1, n),
    color=np.array([hls_to_rgb(2/3 * i/(n-1), 0.5, 1) for i in range(n)]),
    marker_type=monkey,
    radius_bottom=1,
    radius_top=3,
    marker_scale=[5]*3,
    marker_rotation=np.array([np.zeros(n), np.zeros(n), np.pi/2 + np.linspace(0, 4 * np.pi, n)]).T,
)

image info

Sphere markers

You can get perfect spheres as markers by passing in marker_type="spheres". Though note that these are only visible in the rendered view and with the rendering engine set to cycles

import numpy as np
import blender_plots as bplt
n = int(1e2)
bplt.Scatter(
    np.random.rand(n, 3)*50,
    color=np.random.rand(n, 3),
    marker_type="spheres",
    radius=1.5
)

image info

blender-plots's People

Contributors

linusnie avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

blender-plots's Issues

Duplicate colors issue

When I run your code verbatim:

import numpy as np
import blender_plots as bplt
n, l = 150, 100
x, y = np.meshgrid(np.linspace(0, l, n), np.linspace(0, l, n))
x, y = x.ravel(), y.ravel()

z = np.sin(2*np.pi * x / l)np.sin(2np.pi * y / l) * 20
bplt.Scatter(x, y, z, color=(1, 0, 0), name="red")

z = np.sin(4*np.pi * x / l)np.sin(4np.pi * y / l) * 20 + 40
bplt.Scatter(x, y, z, color=(0, 0, 1), name="blue")

I now get this error:

Error: Python: Traceback (most recent call last):
File "\Text", line 8, in
TypeError: Scatter.init() got multiple values for argument 'color'

This persists across a complete uninstall and reinstall of both blender and blender-plots, so it may have been introduced in fixing an earlier issue last week.

Am able to use Scatter but without color

Not too familiar with numpy (or blender for that matter) so maybe I am doing something wrong. This is what I have tried...

bplt.Scatter(np.array([ (1,0,0), (3,0,0)]), color=(1,0,0) )

It draws the cubes but no color

AttributeError runing sample code in blender 4.0

While trying the code provided with install document:
import numpy as np
import blender_plots as bplt
n, l = 150, 100
x, y = np.meshgrid(np.linspace(0, l, n), np.linspace(0, l, n))
x, y = x.ravel(), y.ravel()

It gives an error message:

z = np.sin(2*np.pi * x / l)np.sin(2np.pi * y / l) * 20
bplt.Scatter(x, y, z, color=(1, 0, 0), name="red")
"""Create new node group, useful in blender 3.2 since node group is not added to node modifiers by default."""
group = bpy.data.node_groups.new("Geometry Nodes", 'GeometryNodeTree')
--> group.inputs.new('NodeSocketGeometry', "Geometry")
group.outputs.new('NodeSocketGeometry', "Geometry")
input_node = group.nodes.new('NodeGroupInput')

AttributeError: 'GeometryNodeTree' object has no attribute 'inputs'

Is blender 4.0 supported? Thanks.

Can't install add-in

I'm keen to use this for vis, but I have been unable to install this as per instructions. Tried initially using Edit>Preferences>Add-ons to install the .zip file in Blender 3.2, but it didn't appear in refreshed add-on list but with no error messages.

I then install 3.1.2 which had same problem, and finally cloned the repo which give the following messages in system console on use:

Read prefs: C:\Users\david\AppData\Roaming\Blender Foundation\Blender\3.1\config\userpref.blend
Modules Installed () from 'C:\Users\\Downloads\blender_plots-main.zip' into 'C:\Users\\AppData\Roaming\Blender Foundation\Blender\3.1\scripts\addons'
Info: Deleted 1 object(s)

Traceback (most recent call last):
File "\Text", line 7, in
AttributeError: module 'blender_plots' has no attribute 'Scatter'
Error: Python script failed, check the message in the system console

I'm on windows 11. Are you able to provide any guidance please?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.