diff --git a/.github/workflows/molecular-build.yml b/.github/workflows/molecular-build.yml new file mode 100644 index 0000000..b6a6bb5 --- /dev/null +++ b/.github/workflows/molecular-build.yml @@ -0,0 +1,135 @@ +# This workflow will install Python dependencies, run tests and lint with a single version of Python +# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions + +name: Molecular Addon for Blender + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +jobs: + build_windows: + + runs-on: windows-latest + + steps: + - uses: actions/checkout@v2 + - name: Set up Python 3.11 + uses: actions/setup-python@v2 + with: + python-version: "3.11" + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install flake8 pytest cython==3.0.0 + # if [ -f requirements.txt ]; then pip install -r requirements.txt; fi + - name: Lint with flake8 + run: | + # stop the build if there are Python syntax errors or undefined names + flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics + # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide + flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics + - name: Build with Cython + id: build + # run: echo "::set-output name=version::$(python make_release.py)" + # force bash shell on windows too + shell: bash + run: echo "version=$(python make_release.py)" >> $GITHUB_OUTPUT + - name: Upload windows zip + uses: actions/upload-artifact@v4 + with: + path: molecular_${{ steps.build.outputs.version }}_win.zip + name: molecular_win + + build_linux: + + runs-on: ubuntu-22.04 + + steps: + - uses: actions/checkout@v2 + - name: Set up Python 3.11 + uses: actions/setup-python@v2 + with: + python-version: "3.11" + - name: Install dependencies + run: | + # attempt to install libpython.a for 3.11 + sudo add-apt-repository ppa:deadsnakes/ppa + sudo apt update + sudo apt install libpython3.11-dev + python -m pip install --upgrade pip + pip install flake8 pytest cython==3.0.0 + - name: Lint with flake8 + run: | + # stop the build if there are Python syntax errors or undefined names + flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics + # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide + flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics + - name: Build with Cython + id: build + #run: echo "::set-output name=version::$(python make_release.py)" + run: echo "version=$(python make_release.py)" >> $GITHUB_OUTPUT + - name: Upload linux zip + uses: actions/upload-artifact@v4 + with: + path: molecular_${{ steps.build.outputs.version }}_linux.zip + name: molecular_linux + + build_macos: + + runs-on: macos-latest + + steps: + - uses: actions/checkout@v2 + - name: Set up Python 3.11 + uses: actions/setup-python@v2 + with: + python-version: "3.11" + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install flake8 pytest cython==3.0.0 + - name: Lint with flake8 + run: | + # stop the build if there are Python syntax errors or undefined names + flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics + # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide + flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics + - name: Build with Cython + id: build + #run: echo "::set-output name=version::$(python make_release.py)" + run: echo "version=$(python make_release.py)" >> $GITHUB_OUTPUT + - name: Upload mac zip + uses: actions/upload-artifact@v4 + with: + path: molecular_${{ steps.build.outputs.version }}_mac.zip + name: molecular_mac + + upload_draft: + + name: Upload zips as draft release assets + runs-on: ubuntu-latest + needs: [build_windows, build_linux, build_macos] + + steps: + - uses: actions/checkout@v3 + + - uses: actions/download-artifact@v4 + with: + name: molecular_win + + - uses: actions/download-artifact@v4 + with: + name: molecular_linux + + - uses: actions/download-artifact@v4 + with: + name: molecular_mac + + - name: Uploading + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITHUB_REPOSITORY: $GITHUB_REPOSITORY + run: python .github/workflows/release.py --path . --files `ls *.zip` diff --git a/.github/workflows/release.py b/.github/workflows/release.py new file mode 100644 index 0000000..27747e8 --- /dev/null +++ b/.github/workflows/release.py @@ -0,0 +1,49 @@ +import requests, argparse, json, os + +parser = argparse.ArgumentParser(description='Parameters.') +parser.add_argument('--path', metavar='path', type=str, nargs='+', + help='path where to search for files') +parser.add_argument('--files', metavar='files', type=str, nargs='+', + help='name of files to upload') +args = parser.parse_args() +d = vars(args) + +r = os.environ['GITHUB_REPOSITORY'] +t = os.environ['GITHUB_TOKEN'] +p = d['path'][0] +fi = d['files'] + +owner = r.split("/")[0] +name = r.split("/")[1] + +j = {"tag_name":"v1.0.0", # can be manually edited on publishing this draft ! + "target_commitish":"master", + "name": name, + "body":"Autogenerated release package", + "draft":True, + "prerelease":False, + "generate_release_notes":False}, + +js = json.dumps(j[0]) + +r = requests.post( + headers={'Accept': 'application/vnd.github.v3+json', + 'Authorization': 'token {token}'.format(token=t)}, + url='https://api.github.com/repos/{owner}/{name}/releases'.format(owner=owner, name=name), + data=js, +) + +if "upload_url" in r.json(): + u = r.json()["upload_url"].split("{")[0] + +for f in fi: + pr = os.path.realpath(p) + pa = os.path.join(pr, f) + with open(pa, 'rb') as data: + r = requests.post( + headers={'Content-Type': 'application/zip', + 'Authorization': 'token {token}'.format(token=t)}, + url=u+'?name={name}'.format(name=f), + data=data.read() + ) + diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f849c7e --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +__pycache__ +*.pyd +build +*.c +*.html +.vscode +*.zip diff --git a/README.md b/README.md index 13cfce9..166de38 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,11 @@ +![Molecular Addon for Blender](https://github.com/scorpion81/Blender-Molecular-Script/workflows/Molecular%20Addon%20for%20Blender/badge.svg) + Blender-Molecular-Script ======================== -This is my molecular python script for blender.Inspired from the 2d simulator Really (1998) and Lagoa Multiphysic in Softimage. - -To download the latest version go on my blog: +This is the molecular python script for blender, originally coded py Pyroevil. Inspired from the 2d simulator Really (1998) and Lagoa Multiphysic in Softimage. -http://pyroevil.com/molecular-script-download/ +To download the latest version go to the release section of this repository. The script can be installed like any other addons in Blender: First way: @@ -25,7 +25,53 @@ To activate the plugins: - In the "Object" categories, you must find "Molecular" - Check it , close the "Blender user preferences" windows and have fun. +How to manually build Molecular: +-------------------------------- + +In order for a manual recompilation clone this repo and just run + +`python make_release.py` + +from within the "Blender-Molecular-Script" folder on your desired platform, + +with a python version matching blender's own being installed. +(and cython, too. install with pip for example) + + +Or alternatively, run + +`python setup.py build_ext --inplace` + +from the "sources" folder. + + +How to add UVs to molecular particle instances: +---------------------------------------------- + +This didnt seem to work in blender 3.2.x any more and was fixed in 1.1.4. +Should also apply to blender 3.1.x but that was not tested by me. + +- Original Link: +https://blenderartists.org/t/moleculars-physics/521682/269 + +- Example File: +[test_UV_molecular.zip](https://github.com/scorpion81/Blender-Molecular-Script/files/9320719/test_UV_molecular.zip) + +When the UV data is being baked during the simulation, it will be written into the angular velocity cache part +of the particle system at the moment. + +Hence you can retrieve it via a particle info node later on in order to feed it into the vector socket of the +texture node. Note that the Particle Info Node belongs to the Material on the Sphere aka the object which is being instanced. +In the example blend file there is also a Particle Info Node on the Cube material, but it doesnt really belong there. Rather +it was added by mistake. + +Example with 10x10x10 Grid: +![molecular_bake_uv](https://user-images.githubusercontent.com/1172149/184380338-b07cb5de-4d54-45e0-9fa8-2967f4fb29cc.jpeg) -Visit my blog about this script for donation, comments or more detail : http://pyroevil.com +Same Example with 30x30x30 Grid +![molecular_bake_uv_high](https://user-images.githubusercontent.com/1172149/184380364-9e4a1ff9-8924-4619-9d77-f191950c52e7.jpeg) -You can find older version in the release folder. +Remarks: +- only in cycles +- only in rendered viewport and render +- need to re-bake the sim for UVs after loading (not persistent, it seems) diff --git a/ToDo.txt b/ToDo.txt deleted file mode 100644 index bb13b83..0000000 --- a/ToDo.txt +++ /dev/null @@ -1,31 +0,0 @@ -[x] fix zero devision can happen in somes normal cases -[x] Separate compression / expansion stiffness -[x] Link tension ( bigger or smaller than when is created ) -[x] Max links per particles option -[x] Bake when finish option -[x] start render when finish option -[x] Free the memory when finish or stopped -[x] Max velocity info to give some keys on how much substeps needed -[x] Multithread kdtree searching -[x] Multithread kdtree creation -[x] Link search distance relative to particles size option -[x] Time scale instead of FPS -[x] Fix UI bug when object without pSystem are selected. -[x] Print: new links at this frames , total links from beginning and links broken -[x] Fix bug when mass by density is not check -[x] Fix more than 24hours estimating time issue -[ ] Support links length on randomly sized particles. -[x] Fix memory crash issues. -[x] Add collision "damping". -[ ] Make broken links with particles penetrating don't explode. - -[wip] ***Deeper profiling before doing in the list below*** -[ ] detect INF value in location and velocity -[ ] Try to see if I can send info of particles by memory adress to gain speed (like Mohamed does for it's mesher addon) -[wip] Optimisation in the code ( kdtree search and creation take most of the time when profiling ) -[ ] clean and comments the code -[x] Multithread particles collisions and links solving -[ ] Optimize my own quicksort to stop when median is find -[ ] make particles not born or dead don't be calculated in kdtree -[ ] optimize links searching by puting dead links at the end of array and calloc them when iteration finish -[ ] Make link exponent work more propertly diff --git a/make_release.py b/make_release.py new file mode 100644 index 0000000..bd0991b --- /dev/null +++ b/make_release.py @@ -0,0 +1,74 @@ +from molecular import bl_info +from zipfile import ZipFile, ZIP_DEFLATED +from os import path, walk, remove, chdir, getcwd +import shutil +import platform +import sys +import pathlib +from subprocess import Popen, PIPE + +is_linux = platform.architecture()[1] == "ELF" or platform.system() == "Linux" +is_windows = platform.architecture()[1] == "WindowsPE" or platform.system() == "Windows" + +#in python 3.8.x, sys.abiflags attribute doesnt seem to exist any more instead of returning empty string. +#so better check for existence here before accessing it. +abiflags = '' +if hasattr(sys, 'abiflags'): + abiflags = sys.abiflags + +v = str(sys.version_info.major) + str(sys.version_info.minor) + abiflags + +name = 'mac' +if is_linux: + name = 'linux' +elif is_windows: + name = 'win' + +chdir(getcwd()+"//sources") + +#TODO, blenders (or a compatible) python bin needs to be in $PATH, and if you use blender's you need to copy the python includes from SVN +#into the include folder of blenders python, too + +version = '.'.join(map(str, bl_info['version'])) + +with Popen([sys.executable, "setup.py", "build_ext", "--inplace"], stdout=PIPE) as proc: + proc.stdout.read() + if is_linux: #TODO, test + shutil.move("core.cpython-{}-x86_64-linux-gnu.so".format(v), "..//molecular//core.cpython-{}-x86_64-linux-gnu.so".format(v)) + elif is_windows: + shutil.move("core.cp{}-win_amd64.pyd".format(v), "..//molecular//core.cp{}-win_amd64.pyd".format(v)) + else: + shutil.move("core.cpython-{}-darwin.so".format(v), "..//molecular//core.cpython-{}-darwin.so".format(v)) + + chdir("..") + + with ZipFile('molecular_{}_'.format(('.'.join(map(str, bl_info['version'])))) + name +'.zip', 'w') as z: + for root, _, files in walk('molecular'): + for file in files: + if not file.endswith('.py') and not file.endswith('.so') and not file.endswith('.pyd'): + continue + z.write(path.join(root, file), compress_type=ZIP_DEFLATED) + #cleanup + chdir(getcwd()+"//molecular") + try: + if is_linux: + remove("core.cpython-{}-x86_64-linux-gnu.so".format(v)) + elif is_windows: + remove("core.cp{}-win_amd64.pyd".format(v)) + else: + remove("core.cpython-{}-darwin.so".format(v)) + except: + pass + chdir("..") + chdir(getcwd()+"//sources") + + try: + remove("core.html") + remove("core.c") + shutil.rmtree("build") + except: + pass + + chdir("..") + +print(version) diff --git a/molecular/__init__.py b/molecular/__init__.py index b72cae9..ae03137 100644 --- a/molecular/__init__.py +++ b/molecular/__init__.py @@ -17,841 +17,52 @@ #======================= END GPL LICENSE BLOCK ======================== bl_info = { - "name": "Molecular script", - "author": "Jean-Francois Gallant(PyroEvil)", - "version": (1, 0, 1), - "blender": (2, 6, 8), + "name": "Molecular", + "author": + "Jean-Francois Gallant (PyroEvil), " + "Pavel_Blend, " + "Martin Felke (scorpion81)", + "version": (1, 1, 5), + "blender": (4, 3, 2), "location": "Properties editor > Particles Tabs", - "description": ("Molecular script"), + "description": + "Addon for calculating collisions " + "and for creating links between particles", "warning": "", # used for warning icon and text in addons panel "wiki_url": "http://pyroevil.com/molecular-script-docs/", "tracker_url": "http://pyroevil.com/contact/" , - "category": "Object"} - -import bpy -try: - from molecular import cmolcore -except: - print("cmolcore not working") -from random import random -from math import pi -from mathutils import Vector -from mathutils.geometry import barycentric_transform as barycentric -import imp -from time import clock,sleep,strftime,gmtime -import pstats, cProfile -import multiprocessing + "category": "Physics" +} -mol_simrun = False -def define_props(): - - parset = bpy.types.ParticleSettings - parset.mol_active = bpy.props.BoolProperty(name = "mol_active", description = "Activate molecular script for this particles system",default = False) - parset.mol_refresh = bpy.props.BoolProperty(name = "mol_refresh", description = "Simple property used to refresh data in the process",default = True) - parset.mol_density_active = bpy.props.BoolProperty(name="mol_density_active", description="Control particle weight by density",default = False) - item = [("-1","custom","put your parameter below"),("1555","sand","1555kg per meter cu"),("1000","water","1000kg per meter cu"),("7800","iron","7800kg per meter cu")] - parset.mol_matter = bpy.props.EnumProperty(items = item, description = "Choose a matter preset for density") - parset.mol_density = bpy.props.FloatProperty(name = "mol_density", description = "Density of the matter kg/cube meter", default = 1000, min = 0.001) - - parset.mol_selfcollision_active = bpy.props.BoolProperty(name = "mol_selfcollision_active", description = "Activate self collsion between particles in the system",default = False) - parset.mol_othercollision_active = bpy.props.BoolProperty(name = "mol_othercollision_active", description = "Activate collision with particles from others systems",default = False) - parset.mol_friction = bpy.props.FloatProperty(name = "mol_friction", description = "Friction between particles at collision 0 = no friction , 1 = full friction",default = 0.005 , min = 0 , max = 1) - parset.mol_collision_damp = bpy.props.FloatProperty(name = "mol_collision_damp", description = "Damping between particles at collision 0 = bouncy , 1 = no collision",default = 0.005 , min = 0 , max = 1) - item = [] - for i in range(1,12): - item.append((str(i),"Collision Group " + str(i),"collide only with group " + str(i) )) - parset.mol_collision_group = bpy.props.EnumProperty(items = item, description = "Choose a collision group you want to collide with") - - parset.mol_links_active = bpy.props.BoolProperty(name = "mol_links_active", description = "Activate links between particles of this system",default = False) - parset.mol_link_rellength = bpy.props.BoolProperty(name = "mol_link_rellength", description = "Activate search distance relative to particles radius",default = True) - parset.mol_link_friction = bpy.props.FloatProperty(name = "mol_link_friction", description = "Friction in links , a kind of viscosity. Slow down tangent velocity. 0 = no friction , 1.0 = full of friction",min = 0,max = 1, default = 0.005) - parset.mol_link_length = bpy.props.FloatProperty(name = "mol_link_length", description = "Searching range to make a link between particles",min = 0, precision = 6, default = 1) - parset.mol_link_tension = bpy.props.FloatProperty(name = "mol_link_tension", description = "Make link bigger or smaller than it's created (1 = normal , 0.9 = 10% smaller , 1.15 = 15% bigger)",min = 0, precision = 3, default = 1) - parset.mol_link_tensionrand = bpy.props.FloatProperty(name = "mol_link_tensionrand", description = "Tension random",min = 0,max = 1, precision = 3, default = 0) - parset.mol_link_max = bpy.props.IntProperty(name = "mol_link_max", description = "Maximum of links per particles",min = 0,default = 16) - parset.mol_link_stiff = bpy.props.FloatProperty(name = "mol_link_stiff", description = "Stiffness of links between particles",min = 0,max = 1, default = 1) - parset.mol_link_stiffrand = bpy.props.FloatProperty(name = "mol_link_stiffrand", description = "Random variation for stiffness",min = 0 ,max = 1 ,default = 0) - parset.mol_link_stiffexp = bpy.props.IntProperty(name = "mol_link_stiffexp", description = "Give a exponent force to the spring links", default = 1, min = 1 , max = 10) - parset.mol_link_damp = bpy.props.FloatProperty(name = "mol_link_damp", description = "Damping effect on spring links",min = 0,max = 1, default = 1) - parset.mol_link_damprand = bpy.props.FloatProperty(name = "mol_link_damprand", description = "Random variation on damping",min = 0 ,max = 1, default = 0) - parset.mol_link_broken = bpy.props.FloatProperty(name = "mol_link_broken", description = "How much link can stretch before they broken. 0.01 = 1% , 0.5 = 50% , 2.0 = 200% ...",min = 0, default = 0.5, precision = 3) - parset.mol_link_brokenrand = bpy.props.FloatProperty(name = "mol_link_brokenrand", description = "Give a random variation to the stretch limit",min = 0 ,max = 1, default = 0) - - parset.mol_link_samevalue = bpy.props.BoolProperty(name = "mol_link_samevalue", description = "When active , expansion and compression of the spring have same value",default = True) - - parset.mol_link_estiff = bpy.props.FloatProperty(name = "mol_link_estiff", description = "Expension stiffness of links between particles",min = 0,max = 1, default = 1) - parset.mol_link_estiffrand = bpy.props.FloatProperty(name = "mol_link_estiffrand", description = "Random variation for expansion stiffness",min = 0 ,max = 1 ,default = 0) - parset.mol_link_estiffexp = bpy.props.IntProperty(name = "mol_link_estiffexp", description = "Give a exponent force to the expension spring links", default = 1, min = 1 , max = 10) - parset.mol_link_edamp = bpy.props.FloatProperty(name = "mol_link_edamp", description = "Damping effect on expension spring links",min = 0,max = 1, default = 1) - parset.mol_link_edamprand = bpy.props.FloatProperty(name = "mol_link_edamprand", description = "Random variation on expension damping",min = 0 ,max = 1, default = 0) - parset.mol_link_ebroken = bpy.props.FloatProperty(name = "mol_link_ebroken", description = "How much link can expand before they broken. 0.01 = 1% , 0.5 = 50% , 2.0 = 200% ...",min = 0, default = 0.5, precision = 3) - parset.mol_link_ebrokenrand = bpy.props.FloatProperty(name = "mol_link_ebrokenrand", description = "Give a random variation to the expension stretch limit",min = 0 ,max = 1, default = 0) - - - item = [] - for i in range(1,12): - item.append((str(i),"Relink Group " + str(i),"Relink only with group " + str(i) )) - parset.mol_relink_group = bpy.props.EnumProperty(items = item, description = "Choose a group that new link are possible") - parset.mol_relink_chance = bpy.props.FloatProperty(name = "mol_relink_chance", description = "Chance of a new link are created on collision. 0 = off , 100 = 100% of chance",min = 0, max = 100, default = 0) - parset.mol_relink_chancerand = bpy.props.FloatProperty(name = "mol_relink_chancerand", description = "Give a random variation to the chance of new link", default = 0) - parset.mol_relink_tension = bpy.props.FloatProperty(name = "mol_relink_tension", description = "Make link bigger or smaller than it's created (1 = normal , 0.9 = 10% smaller , 1.15 = 15% bigger)",min = 0, precision = 3, default = 1) - parset.mol_relink_tensionrand = bpy.props.FloatProperty(name = "mol_relink_tensionrand", description = "Tension random",min = 0,max = 1, precision = 3, default = 0) - parset.mol_relink_max = bpy.props.IntProperty(name = "mol_relink_max", description = "Maximum of links per particles",min = 0,default = 16) - parset.mol_relink_stiff = bpy.props.FloatProperty(name = "mol_relink_stiff", description = "Stiffness of links between particles",min = 0,max = 1, default = 1) - parset.mol_relink_stiffrand = bpy.props.FloatProperty(name = "mol_relink_stiffrand", description = "Random variation for stiffness",min = 0, max = 1 ,default = 0) - parset.mol_relink_stiffexp = bpy.props.IntProperty(name = "mol_relink_stiffexp", description = "Give a exponent force to the spring links",min = 1, max = 10, default = 1) - parset.mol_relink_damp = bpy.props.FloatProperty(name = "mol_relink_damp", description = "Damping effect on spring links",min = 0, max = 1, default = 1) - parset.mol_relink_damprand = bpy.props.FloatProperty(name = "mol_relink_damprand", description = "Random variation on damping",min = 0 , max = 1, default = 0) - parset.mol_relink_broken = bpy.props.FloatProperty(name = "mol_relink_broken", description = "How much link can stretch before they broken. 0.01 = 1% , 0.5 = 50% , 2.0 = 200% ...",min = 0, default = 0.5, precision = 3) - parset.mol_relink_brokenrand = bpy.props.FloatProperty(name = "mol_relink_brokenrand", description = "Give a random variation to the stretch limit",min = 0, max = 1, default = 0) - - parset.mol_relink_samevalue = bpy.props.BoolProperty(name = "mol_relink_samevalue", description = "When active , expansion and compression of the spring have same value",default = True) - - parset.mol_relink_estiff = bpy.props.FloatProperty(name = "mol_relink_estiff", description = "Stiffness of links expension between particles",min = 0,max = 1, default = 1) - parset.mol_relink_estiffrand = bpy.props.FloatProperty(name = "mol_relink_estiffrand", description = "Random variation for expension stiffness",min = 0, max = 1 ,default = 0) - parset.mol_relink_estiffexp = bpy.props.IntProperty(name = "mol_relink_estiffexp", description = "Give a exponent force to the spring links",min = 1, max = 10, default = 1) - parset.mol_relink_edamp = bpy.props.FloatProperty(name = "mol_relink_edamp", description = "Damping effect on expension spring links",min = 0, max = 1, default = 1) - parset.mol_relink_edamprand = bpy.props.FloatProperty(name = "mol_relink_deamprand", description = "Random variation on damping",min = 0 , max = 0, default = 0) - parset.mol_relink_ebroken = bpy.props.FloatProperty(name = "mol_relink_ebroken", description = "How much link can stretch before they broken. 0.01 = 1% , 0.5 = 50% , 2.0 = 200% ...",min = 0, default = 0.5, precision = 3) - parset.mol_relink_ebrokenrand = bpy.props.FloatProperty(name = "mol_relink_ebrokenrand", description = "Give a random variation to the stretch limit",min = 0, max = 1, default = 0) - - parset.mol_var1 = bpy.props.IntProperty(name = "mol_var1", description = "Current number of particles to calculate substep",min = 1, default = 1000) - parset.mol_var2 = bpy.props.IntProperty(name = "mol_var2", description = "Current substep",min = 1, default = 4) - parset.mol_var3 = bpy.props.IntProperty(name = "mol_var3", description = "Targeted number of particles you want to increase or decrease from current system to calculate substep you need to achieve similar effect",min = 1, default = 1000) - parset.mol_bakeuv = bpy.props.BoolProperty(name = "mol_bakeuv", description = "Bake uv when finish",default = False) - - bpy.types.Scene.mol_timescale_active = bpy.props.BoolProperty(name = "mol_timescale_active", description = "Activate TimeScaling",default = False) - bpy.types.Scene.timescale = bpy.props.FloatProperty(name = "timescale", description = "SpeedUp or Slow down the simulation with this multiplier", default = 1) - bpy.types.Scene.mol_substep = bpy.props.IntProperty(name = "mol_substep", description = "mol_substep. Higher equal more stable and accurate but more slower",min = 0, max = 900, default = 4) - bpy.types.Scene.mol_bake = bpy.props.BoolProperty(name = "mol_bake", description = "Bake simulation when finish",default = True) - bpy.types.Scene.mol_render = bpy.props.BoolProperty(name = "mol_render", description = "Start rendering animation when simulation is finish. WARNING: It's freeze blender until render is finish.",default = False) - bpy.types.Scene.mol_cpu = bpy.props.IntProperty(name = "mol_cpu", description = "Numbers of cpu's included for process the simulation", default = multiprocessing.cpu_count(),min = 1,max =multiprocessing.cpu_count()) - -def pack_data(initiate): - global mol_exportdata - global mol_minsize - global mol_simrun - psyslen = 0 - parnum = 0 - scene = bpy.context.scene - for obj in bpy.data.objects: - for psys in obj.particle_systems: - if psys.settings.mol_matter != "-1": - psys.settings.mol_density = float(psys.settings.mol_matter) - if psys.settings.mol_active == True and len(psys.particles) > 0: - parlen = len(psys.particles) - par_loc = [0,0,0] * parlen - par_vel = [0,0,0] * parlen - par_size = [0] * parlen - par_alive = [] - for par in psys.particles: - parnum += 1 - if par.alive_state == "UNBORN": - par_alive.append(2) - if par.alive_state == "ALIVE": - par_alive.append(0) - if par.alive_state == "DEAD": - par_alive.append(3) - - psys.particles.foreach_get('location',par_loc) - psys.particles.foreach_get('velocity',par_vel) - - if initiate: - par_mass = [] - - if psys.settings.mol_density_active: - for par in psys.particles: - par_mass.append(psys.settings.mol_density * (4/3*pi*((par.size/2)**3))) - else: - for par in psys.particles: - par_mass.append(psys.settings.mass) - """ - if scene.mol_timescale_active == True: - psys.settings.timestep = 1 / (scene.render.fps / scene.timescale) - else: - psys.settings.timestep = 1 / scene.render.fps - """ - #psys.settings.count = psys.settings.count - psys.point_cache.frame_step = psys.point_cache.frame_step - psyslen += 1 - psys.particles.foreach_get('size',par_size) - if mol_minsize > min(par_size): - mol_minsize = min(par_size) - - if psys.settings.mol_link_samevalue: - psys.settings.mol_link_estiff = psys.settings.mol_link_stiff - psys.settings.mol_link_estiffrand = psys.settings.mol_link_stiffrand - psys.settings.mol_link_estiffexp = psys.settings.mol_link_stiffexp - psys.settings.mol_link_edamp = psys.settings.mol_link_damp - psys.settings.mol_link_edamprand = psys.settings.mol_link_damprand - psys.settings.mol_link_ebroken = psys.settings.mol_link_broken - psys.settings.mol_link_ebrokenrand = psys.settings.mol_link_brokenrand - - if psys.settings.mol_relink_samevalue: - psys.settings.mol_relink_estiff = psys.settings.mol_relink_stiff - psys.settings.mol_relink_estiffrand = psys.settings.mol_relink_stiffrand - psys.settings.mol_relink_estiffexp = psys.settings.mol_relink_stiffexp - psys.settings.mol_relink_edamp = psys.settings.mol_relink_damp - psys.settings.mol_relink_edamprand = psys.settings.mol_relink_damprand - psys.settings.mol_relink_ebroken = psys.settings.mol_relink_broken - psys.settings.mol_relink_ebrokenrand = psys.settings.mol_relink_brokenrand - - params = [0] * 45 - params[0] = psys.settings.mol_selfcollision_active - params[1] = psys.settings.mol_othercollision_active - params[2] = psys.settings.mol_collision_group - params[3] = psys.settings.mol_friction - params[4] = psys.settings.mol_collision_damp - params[5] = psys.settings.mol_links_active - if psys.settings.mol_link_rellength == True: - params[6] = psys.settings.particle_size * psys.settings.mol_link_length - else: - params[6] = psys.settings.mol_link_length - params[7] = psys.settings.mol_link_max - params[8] = psys.settings.mol_link_tension - params[9] = psys.settings.mol_link_tensionrand - params[10] = psys.settings.mol_link_stiff - params[11] = psys.settings.mol_link_stiffrand - params[12] = psys.settings.mol_link_stiffexp - params[13] = psys.settings.mol_link_damp - params[14] = psys.settings.mol_link_damprand - params[15] = psys.settings.mol_link_broken - params[16] = psys.settings.mol_link_brokenrand - params[17] = psys.settings.mol_link_estiff - params[18] = psys.settings.mol_link_estiffrand - params[19] = psys.settings.mol_link_estiffexp - params[20] = psys.settings.mol_link_edamp - params[21] = psys.settings.mol_link_edamprand - params[22] = psys.settings.mol_link_ebroken - params[23] = psys.settings.mol_link_ebrokenrand - params[24] = psys.settings.mol_relink_group - params[25] = psys.settings.mol_relink_chance - params[26] = psys.settings.mol_relink_chancerand - params[27] = psys.settings.mol_relink_max - params[28] = psys.settings.mol_relink_tension - params[29] = psys.settings.mol_relink_tensionrand - params[30] = psys.settings.mol_relink_stiff - params[31] = psys.settings.mol_relink_stiffexp - params[32] = psys.settings.mol_relink_stiffrand - params[33] = psys.settings.mol_relink_damp - params[34] = psys.settings.mol_relink_damprand - params[35] = psys.settings.mol_relink_broken - params[36] = psys.settings.mol_relink_brokenrand - params[37] = psys.settings.mol_relink_estiff - params[38] = psys.settings.mol_relink_estiffexp - params[39] = psys.settings.mol_relink_estiffrand - params[40] = psys.settings.mol_relink_edamp - params[41] = psys.settings.mol_relink_edamprand - params[42] = psys.settings.mol_relink_ebroken - params[43] = psys.settings.mol_relink_ebrokenrand - params[44] = psys.settings.mol_link_friction - - if initiate: - mol_exportdata[0][2] = psyslen - mol_exportdata[0][3] = parnum - #print(par_loc) - mol_exportdata.append((parlen,par_loc,par_vel,par_size,par_mass,par_alive,params)) - pass - else: - #print(par_loc) - mol_exportdata.append((par_loc,par_vel,par_alive)) - pass - - - -class MolecularPanel(bpy.types.Panel): - """Creates a Panel in the Object properties window""" - bl_label = "Molecular script" - bl_idname = "OBJECT_PT_molecular" - bl_space_type = 'PROPERTIES' - bl_region_type = 'WINDOW' - bl_context = "particle" - - def draw_header(self, context): - layout = self.layout - obj = context.object - psys = obj.particle_systems.active - row = layout.row() - if psys != None: - row.prop(psys.settings,"mol_active", text = "") - - def draw(self, context): - global mol_simrun - global mol_timeremain - global mol_objuvbake - global mol_psysuvbake - - layout = self.layout - scn = bpy.context.scene - obj = context.object - psys = obj.particle_systems.active - if psys != None: - layout.enabled = psys.settings.mol_active - row = layout.row() - row.label(text = "Density:") - box = layout.box() - box.prop(psys.settings,"mol_density_active", text = "Calculate particles weight by density") - subbox = box.box() - subbox.enabled = psys.settings.mol_density_active - row = subbox.row() - row.prop(psys.settings,"mol_matter",text = "Preset:") - row = subbox.row() - if int(psys.settings.mol_matter) == 0: - row.enabled = True - elif int(psys.settings.mol_matter) >= 1: - row.enabled = False - row.prop(psys.settings, "mol_density", text = "Kg per CubeMeter:") - #subsubbox = subbox.box() - #row = subsubbox.row() - #row.label(text = "Particle info:") - #row = subsubbox.row() - #row.label(icon = "INFO",text = "size: " + str(round(psys.settings.particle_size,5)) + " m") - #row.label(icon = "INFO",text = "volume: " + str(round(psys.settings.particle_size**3,5)) + " m3") - #row = subsubbox.row() - pmass = (psys.settings.particle_size**3) * psys.settings.mol_density - #row.label(icon = "INFO",text = "mass: " + str(round(pmass,5)) + " kg") - row = subbox.row() - row.label(icon = "INFO",text = "Total system approx weight: " + str(round(len(psys.particles) * pmass,4)) + " kg") - row = layout.row() - row.label(text = "Collision:") - box = layout.box() - box.prop(psys.settings,"mol_selfcollision_active", text = "Activate Self Collision") - box.prop(psys.settings,"mol_othercollision_active", text = "Activate Collision with others") - box.prop(psys.settings,"mol_collision_group",text = " Collide only with:") - box.prop(psys.settings,"mol_friction",text = " Friction:") - box.prop(psys.settings,"mol_collision_damp",text = " Damping:") - - row = layout.row() - row.label(text = "Links:") - box = layout.box() - box.prop(psys.settings,"mol_links_active", text = "Activate Particles linking") - - subbox = box.box() - subbox.enabled = psys.settings.mol_links_active - subbox.label(text = "Initial Linking (at bird):") - row = subbox.row() - row.prop(psys.settings,"mol_link_length",text = "search length") - row.prop(psys.settings,"mol_link_rellength",text = "Relative") - row = subbox.row() - row.prop(psys.settings,"mol_link_max",text = "Max links") - row = subbox.row() - row.prop(psys.settings,"mol_link_friction",text = "Link friction") - layout.separator() - row = subbox.row() - row.prop(psys.settings,"mol_link_tension",text = "Tension") - row.prop(psys.settings,"mol_link_tensionrand",text = "Rand Tension") - row = subbox.row() - row.prop(psys.settings,"mol_link_stiff",text = "Stiff") - row.prop(psys.settings,"mol_link_stiffrand",text = "Rand Stiff") - #row = subbox.row() - #row.prop(psys.settings,"mol_link_stiffexp",text = "Exponent") - #row.label(text = "") - row = subbox.row() - row.prop(psys.settings,"mol_link_damp",text = "Damping") - row.prop(psys.settings,"mol_link_damprand",text = "Rand Damping") - row = subbox.row() - row.prop(psys.settings,"mol_link_broken",text = "broken") - row.prop(psys.settings,"mol_link_brokenrand",text = "Rand Broken") - row = subbox.row() - layout.separator() - row = subbox.row() - row.prop(psys.settings,"mol_link_samevalue", text = "Same values for compression/expansion") - row = subbox.row() - row.enabled = not psys.settings.mol_link_samevalue - row.prop(psys.settings,"mol_link_estiff",text = "E Stiff") - row.prop(psys.settings,"mol_link_estiffrand",text = "Rand E Stiff") - #row = subbox.row() - #row.enabled = not psys.settings.mol_link_samevalue - #row.prop(psys.settings,"mol_link_estiffexp",text = "E Exponent") - #row.label(text = "") - row = subbox.row() - row.enabled = not psys.settings.mol_link_samevalue - row.prop(psys.settings,"mol_link_edamp",text = "E Damping") - row.prop(psys.settings,"mol_link_edamprand",text = "Rand E Damping") - row = subbox.row() - row.enabled = not psys.settings.mol_link_samevalue - row.prop(psys.settings,"mol_link_ebroken",text = "E broken") - row.prop(psys.settings,"mol_link_ebrokenrand",text = "Rand E Broken") - - - subbox = box.box() - subbox.active = psys.settings.mol_links_active - subbox.label(text = "New Linking (at collision):") - row = subbox.row() - row.prop(psys.settings,"mol_relink_group",text = "Only links with:") - row = subbox.row() - row.prop(psys.settings,"mol_relink_chance",text = "% linking") - row.prop(psys.settings,"mol_relink_chancerand",text = "Rand % linking") - row = subbox.row() - row.prop(psys.settings,"mol_relink_max",text = "Max links") - row = subbox.row() - layout.separator() - row = subbox.row() - row.prop(psys.settings,"mol_relink_tension",text = "Tension") - row.prop(psys.settings,"mol_relink_tensionrand",text = "Rand Tension") - row = subbox.row() - row.prop(psys.settings,"mol_relink_stiff",text = "Stiff") - row.prop(psys.settings,"mol_relink_stiffrand",text = "Rand Stiff") - #row = subbox.row() - #row.prop(psys.settings,"mol_relink_stiffexp",text = "Exp") - #row.label(text = "") - row = subbox.row() - row.prop(psys.settings,"mol_relink_damp",text = "Damping") - row.prop(psys.settings,"mol_relink_damprand",text = "Rand Damping") - row = subbox.row() - row.prop(psys.settings,"mol_relink_broken",text = "broken") - row.prop(psys.settings,"mol_relink_brokenrand",text = "Rand broken") - row = subbox.row() - layout.separator() - row = subbox.row() - row.prop(psys.settings,"mol_relink_samevalue", text = "Same values for compression/expansion") - row = subbox.row() - row.enabled = not psys.settings.mol_relink_samevalue - row.prop(psys.settings,"mol_relink_estiff",text = "E Stiff") - row.prop(psys.settings,"mol_relink_estiffrand",text = "Rand E Stiff") - #row = subbox.row() - #row.enabled = not psys.settings.mol_relink_samevalue - #row.prop(psys.settings,"mol_relink_estiffexp",text = "Exp") - #row.label(text = "") - row = subbox.row() - row.enabled = not psys.settings.mol_relink_samevalue - row.prop(psys.settings,"mol_relink_edamp",text = "E Damping") - row.prop(psys.settings,"mol_relink_edamprand",text = "Rand E Damping") - row = subbox.row() - row.enabled = not psys.settings.mol_relink_samevalue - row.prop(psys.settings,"mol_relink_ebroken",text = "E broken") - row.prop(psys.settings,"mol_relink_ebrokenrand",text = "Rand E broken") - - row = layout.row() - row.label(text = "UV's:") - box = layout.box() - row = box.row() - if obj.data.uv_layers.active != None: - row.prop(psys.settings,"mol_bakeuv",text = "Bake UV at ending (current: " + str(obj.data.uv_textures.active.name) + ")" ) - else: - row.active = False - row.prop(psys.settings,"mol_bakeuv",text = "Bake UV at ending (current: None)" ) - - row = layout.row() - row.label(text = "") - row = layout.row() - row.label(text = "Simulate") - row = layout.row() - row.prop(scn,"frame_start",text = "Start Frame") - row.prop(scn,"frame_end",text = "End Frame") - #row = layout.row() - #row.prop(scn,"mol_timescale_active",text = "Activate TimeScaling") - #row = layout.row() - #row.enabled = scn.mol_timescale_active - #row.prop(scn,"timescale",text = "TimeScale") - #row.label(text = "") - row = layout.row() - row.prop(scn,"mol_substep",text = "mol_substep") - row.label(text = "") - row = layout.row() - row.label(text = "CPU used:") - row.prop(scn,"mol_cpu",text = "CPU") - row = layout.row() - row.prop(scn,"mol_bake",text = "Bake all at ending") - row.prop(scn,"mol_render",text = "Render at ending") - row = layout.row() - if mol_simrun == False and psys.point_cache.is_baked == False: - row.enabled = True - row.operator("object.mol_simulate",icon = 'RADIO',text = "Start Molecular Simulation") - row = layout.row() - row.enabled = False - row.operator("ptcache.free_bake_all", text="Free All Bakes") - if psys.point_cache.is_baked == True and mol_simrun == False: - row.enabled = False - row.operator("object.mol_simulate",icon = 'RADIO',text = "Simulation baked") - row = layout.row() - row.enabled = True - row.operator("ptcache.free_bake_all", text="Free All Bakes") - if mol_simrun == True: - row.enabled = False - row.operator("object.mol_simulate",icon = 'RADIO',text = "Process: " + mol_timeremain + " left") - row = layout.row() - row.enabled = False - row.operator("ptcache.free_bake_all", text="Free All Bakes") - - box = layout.box() - row = box.row() - box.enabled = True - row.label(text = "Molecular Tools:",icon = 'MODIFIER') - subbox = box.box() - row = subbox.row() - row.label(text = "Particle UV:") - row = subbox.row() - row.alignment = 'CENTER' - row.label(icon = 'INFO',text = "Set current particles position ") - row = subbox.row() - row.alignment = 'CENTER' - row.label(text = "has global or current uv in angular velocity.") - row = subbox.row() - row.alignment = 'CENTER' - row.label(text = " Retrieve it with Cycles particle info node") - row = subbox.row() - row.operator("object.mol_set_global_uv",icon = 'GROUP_UVS',text = "Set Global UV") - row = subbox.row() - if obj.data.uv_layers.active != None: - mol_objuvbake = context.object - mol_psysuvbake = context.object.particle_systems.active - row.operator("object.mol_set_active_uv",icon = 'GROUP_UVS',text = "Set Active UV (current: " + str(obj.data.uv_textures.active.name) + ")" ) - else: - row.active = False - row.operator("object.mol_set_active_uv",icon = 'GROUP_UVS',text = "Set Active UV ( no uvs found)") - subbox = box.box() - row = subbox.row() - row.label(text = "SUBSTEPS CALCULATOR:") - row = subbox.row() - row.label(icon = 'INFO',text = "Current systems have: " + str(len(psys.particles)) + " particles") - row = subbox.row() - row.prop(psys.settings,"mol_var1",text = "Current numbers of particles") - row = subbox.row() - row.prop(psys.settings,"mol_var2",text = "Current substep") - row = subbox.row() - row.prop(psys.settings,"mol_var3",text = "Targeted numbers of particles") - diff = (psys.settings.mol_var3 / psys.settings.mol_var1) - factor = (psys.settings.mol_var3**(1/3) / psys.settings.mol_var1**(1/3)) - newsubstep = int(round(factor * psys.settings.mol_var2)) - row = subbox.row() - row.label(icon = 'FORWARD',text = "You must set new substep to: " + str(newsubstep)) - row = subbox.row() - row.label(icon = 'ERROR',text = "Multiply particles size by: " + str(round(1/factor,5))) - row = subbox.row() - row.label(icon = 'ERROR',text = "Multiply others sys particle number by: " + str(round(diff,5))) - - - box = layout.box() - row = box.row() - box.active = False - box.alert = False - row.alignment = 'CENTER' - row.label(text = "THANKS TO ALL DONATORS !") - row = box.row() - row.alignment = 'CENTER' - row.label(text = "If you want donate to support my work") - row = box.row() - row.alignment = 'CENTER' - row.operator("wm.url_open", text=" click here to Donate ", icon='URL').url = "www.pyroevil.com/donate/" - row = box.row() - row.alignment = 'CENTER' - row.label(text = "or visit: ") - row = box.row() - row.alignment = 'CENTER' - row.label(text = "www.pyroevil.com/donate/") - - - -class MolSimulate(bpy.types.Operator): - """Tooltip""" - bl_idname = "object.mol_simulate" - bl_label = "Mol Simulate" - - - def execute(self, context): - global mol_substep - global mol_old_endframe - global mol_exportdata - global mol_report - global mol_minsize - global mol_newlink - global mol_deadlink - global mol_totallink - global mol_totaldeadlink - global mol_simrun - global mol_timeremain - - mol_simrun = True - mol_timeremain = "...Simulating..." - - mol_minsize = 1000000000 - - mol_newlink = 0 - mol_deadlink = 0 - mol_totallink = 0 - mol_totaldeadlink = 0 - - print("Molecular Sim start--------------------------------------------------") - mol_stime = clock() - scene = bpy.context.scene - object = bpy.context.object - scene.frame_set(frame = scene.frame_start) - mol_old_endframe = scene.frame_end - mol_substep = scene.mol_substep - scene.render.frame_map_old = 1 - scene.render.frame_map_new = mol_substep + 1 - scene.frame_end *= mol_substep + 1 - - if scene.mol_timescale_active == True: - fps = scene.render.fps / scene.timescale - else: - fps = scene.render.fps - cpu = scene.mol_cpu - mol_exportdata = [] - mol_exportdata = [[fps,mol_substep,0,0,cpu]] - mol_stime = clock() - pack_data(True) - #print("sys number",mol_exportdata[0][2]) - etime = clock() - print(" " + "PackData take " + str(round(etime - mol_stime,3)) + "sec") - mol_stime = clock() - mol_report = cmolcore.init(mol_exportdata) - etime = clock() - print(" " + "Export time take " + str(round(etime - mol_stime,3)) + "sec") - print(" total numbers of particles: " + str(mol_report)) - print(" start processing:") - bpy.ops.wm.mol_simulate_modal() - return {'FINISHED'} - -class MolSetGlobalUV(bpy.types.Operator): - """Tooltip""" - bl_idname = "object.mol_set_global_uv" - bl_label = "Mol Set UV" - - - def execute(self, context): - global mol_substep - global mol_old_endframe - global mol_exportdata - global mol_report - global mol_minsize - global mol_newlink - global mol_deadlink - global mol_totallink - global mol_totaldeadlink - global mol_simrun - global mol_timeremain - scene = bpy.context.scene - object = bpy.context.object - psys = object.particle_systems.active - coord = [0,0,0] * len(psys.particles) - psys.particles.foreach_get("location",coord) - psys.particles.foreach_set("angular_velocity",coord) - - return {'FINISHED'} - - -class MolSetActiveUV(bpy.types.Operator): - """Tooltip""" - bl_idname = "object.mol_set_active_uv" - bl_label = "Mol Set Active UV" - - - def execute(self, context): - global mol_substep - global mol_old_endframe - global mol_exportdata - global mol_report - global mol_minsize - global mol_newlink - global mol_deadlink - global mol_totallink - global mol_totaldeadlink - global mol_simrun - global mol_timeremain - global mol_objuvbake - global mol_psysuvbake - - scene = context.scene - object = mol_objuvbake - - print(' start bake uv from:',object.name) - #object2 = object.copy() - - obdata = object.data.copy() - object2 = bpy.data.objects.new(name="mol_uv_temp",object_data = obdata) - object2.matrix_world = object.matrix_world - - context.scene.objects.link(object2) - mod = object2.modifiers.new("tri_for_uv","TRIANGULATE") - mod.use_beauty = False - newmesh = object2.to_mesh(bpy.context.scene,True,"RENDER",True,False) - object2.data = newmesh - context.scene.update() - """ - oldmesh = object.data - newmesh = object.data.copy() - object.data = newmesh - mod = object.modifiers.new("tri_for_uv","TRIANGULATE") - mod.use_beauty = False - bpy.ops.object.modifier_apply(apply_as='DATA', modifier=mod.name) - """ - psys = mol_psysuvbake - #print('-------------start------------') - for par in psys.particles: - parloc = (par.location * object2.matrix_world) - object2.location - point = object2.closest_point_on_mesh(parloc) - #print('closest:',par.location,point[0],point[2]) - vindex1 = object2.data.polygons[point[2]].vertices[0] - vindex2 = object2.data.polygons[point[2]].vertices[1] - vindex3 = object2.data.polygons[point[2]].vertices[2] - v1 = (object2.matrix_world * object2.data.vertices[vindex1].co).to_tuple() - v2 = (object2.matrix_world * object2.data.vertices[vindex2].co).to_tuple() - v3 = (object2.matrix_world * object2.data.vertices[vindex3].co).to_tuple() - uvindex1 = object2.data.polygons[point[2]].loop_start + 0 - uvindex2 = object2.data.polygons[point[2]].loop_start + 1 - uvindex3 = object2.data.polygons[point[2]].loop_start + 2 - uv1 = object2.data.uv_layers.active.data[uvindex1].uv.to_3d() - uv2 = object2.data.uv_layers.active.data[uvindex2].uv.to_3d() - uv3 = object2.data.uv_layers.active.data[uvindex3].uv.to_3d() - #print(vertices1.co,vertices2.co,vertices3.co) - #print(uv1,uv2,uv3) - p = object2.matrix_world * point[0] - v1 = Vector(v1) - v2 = Vector(v2) - v3 = Vector(v3) - uv1 = Vector(uv1) - uv2 = Vector(uv2) - uv3 = Vector(uv3) - #print(a,b,c,uv1,uv2,uv3,p) - newuv = barycentric(p,v1,v2,v3,uv1,uv2,uv3) - #print('New UVs:',newuv) - parloc = par.location * object2.matrix_world - dist = (Vector((parloc[0] - p[0],parloc[1] - p[1],parloc[2] - p[2]))).length - newuv[2] = dist - newuv = newuv.to_tuple() - par.angular_velocity = newuv - scene.objects.unlink(object2) - bpy.data.objects.remove(object2) - bpy.data.meshes.remove(newmesh) - print(' uv baked on:',psys.settings.name) - - return {'FINISHED'} +def register(): -class MolSimulateModal(bpy.types.Operator): - """Operator which runs its self from a timer""" - bl_idname = "wm.mol_simulate_modal" - bl_label = "Simulate Molecular" - _timer = None + import bpy - def modal(self, context, event): - global mol_substep - global mol_old_endframe - global mol_exportdata - global mol_stime - global mol_importdata - global mol_minsize - global mol_newlink - global mol_deadlink - global mol_totallink - global mol_totaldeadlink - global mol_timeremain - global mol_simrun - global mol_objuvbake - global mol_psysuvbake - - #mol_stime = clock() - scene = bpy.context.scene - frame_end = scene.frame_end - frame_current = scene.frame_current - if event.type == 'ESC' or frame_current == frame_end: - if frame_current == frame_end and scene.mol_bake == True: - fake_context = bpy.context.copy() - for obj in bpy.data.objects: - for psys in obj.particle_systems: - if psys.settings.mol_active == True and len(psys.particles) > 0: - fake_context["point_cache"] = psys.point_cache - bpy.ops.ptcache.bake_from_cache(fake_context) - scene.render.frame_map_new = 1 - scene.frame_end = mol_old_endframe - - for obj in bpy.data.objects: - for psys in obj.particle_systems: - for psys in obj.particle_systems: - if psys.settings.mol_bakeuv == True: - mol_objuvbake = obj - mol_psysuvbake = psys - bpy.context.scene.update() - scene.frame_set(frame = psys.settings.frame_start) - bpy.context.scene.update() - bpy.ops.object.mol_set_active_uv() - - if frame_current == frame_end and scene.mol_render == True: - bpy.ops.render.render(animation=True) - - scene.frame_set(frame = scene.frame_start) + from . import properties, ui, operators - cmolcore.memfree() - mol_simrun = False - print("--------------------------------------------------Molecular Sim end") - return self.cancel(context) + properties.define_props() + bpy.utils.register_class(operators.MolSimulateModal) + bpy.utils.register_class(operators.MolSimulate) + bpy.utils.register_class(operators.MolSetGlobalUV) + bpy.utils.register_class(operators.MolSetActiveUV) + for panel in ui.panel_classes: + bpy.utils.register_class(panel) - if event.type == 'TIMER': - if frame_current == scene.frame_start: - mol_stime = clock() - mol_exportdata = [] - #stimex = clock() - pack_data(False) - #print("packdata time",clock() - stimex,"sec") - mol_importdata = cmolcore.simulate(mol_exportdata) - i = 0 - #stimex = clock() - for obj in bpy.data.objects: - for psys in obj.particle_systems: - if psys.settings.mol_active == True and len(psys.particles) > 0: - #print(len(mol_importdata[i][1])) - #print(len(psys.particles)) - psys.particles.foreach_set('velocity',mol_importdata[1][i]) - i += 1 - #print("inject new velocity time",clock() - stimex,"sec") - framesubstep = frame_current/(mol_substep+1) - if framesubstep == int(framesubstep): - etime = clock() - print(" frame " + str(framesubstep + 1) + ":") - print(" links created:",mol_newlink) - if mol_totallink != 0: - print(" links broked :",mol_deadlink) - print(" total links:",mol_totallink - mol_totaldeadlink ,"/",mol_totallink," (",round((((mol_totallink - mol_totaldeadlink) / mol_totallink) * 100),2),"%)") - print(" Molecular Script: " + str(round(etime - mol_stime,3)) + " sec") - remain = (((etime - mol_stime) * (mol_old_endframe - framesubstep - 1))) - days = int(strftime('%d',gmtime(remain))) - 1 - mol_timeremain = strftime(str(days) + ' days %H hours %M mins %S secs', gmtime(remain)) - print(" Remaining estimated:",mol_timeremain) - mol_newlink = 0 - mol_deadlink = 0 - mol_stime = clock() - stime2 = clock() - mol_newlink += mol_importdata[2] - mol_deadlink += mol_importdata[3] - mol_totallink = mol_importdata[4] - mol_totaldeadlink = mol_importdata[5] - scene.frame_set(frame = frame_current + 1) - if framesubstep == int(framesubstep): - etime2 = clock() - print(" Blender: " + str(round(etime2 - stime2,3)) + " sec") - stime2 = clock() - return {'PASS_THROUGH'} - def execute(self, context): - self._timer = context.window_manager.event_timer_add(0.000000001, context.window) - context.window_manager.modal_handler_add(self) - return {'RUNNING_MODAL'} +def unregister(): - def cancel(self, context): - context.window_manager.event_timer_remove(self._timer) - return {'CANCELLED'} + import bpy + from . import properties, ui, operators -def register(): - define_props() - bpy.utils.register_class(MolSimulateModal) - bpy.utils.register_class(MolSimulate) - bpy.utils.register_class(MolSetGlobalUV) - bpy.utils.register_class(MolSetActiveUV) - bpy.utils.register_class(MolecularPanel) - pass + bpy.utils.unregister_class(operators.MolSimulateModal) + bpy.utils.unregister_class(operators.MolSimulate) + bpy.utils.unregister_class(operators.MolSetGlobalUV) + bpy.utils.unregister_class(operators.MolSetActiveUV) + for panel in reversed(ui.panel_classes): + bpy.utils.unregister_class(panel) -def unregister(): - bpy.utils.unregister_class(MolSimulateModal) - bpy.utils.unregister_class(MolSimulate) - bpy.utils.unregister_class(MolSetGlobalUV) - bpy.utils.unregister_class(MolSetActiveUV) - bpy.utils.unregister_class(MolecularPanel) - pass - - if __name__ == "__main__": register() diff --git a/molecular/cmolcore.pyd b/molecular/cmolcore.pyd deleted file mode 100644 index f7f3773..0000000 Binary files a/molecular/cmolcore.pyd and /dev/null differ diff --git a/molecular/descriptions.py b/molecular/descriptions.py new file mode 100644 index 0000000..a0450db --- /dev/null +++ b/molecular/descriptions.py @@ -0,0 +1,78 @@ +# Matter descriptions +MATTER_CUSTOM = 'put your parameter below' +MATTER_SAND = '1555kg per meter cu' +MATTER_WATER = '1000kg per meter cu' +MATTER_IRON = '7800kg per meter cu' + +# Properties descriptions +ACTIVE = 'Activate molecular script for this particles system' +REFRESH = 'Simple property used to refresh data in the process' + +DENSITY_ACTIVE = 'Control particle weight by density' +MATTER = 'Choose a matter preset for density' +DENSITY = 'Density of the matter kg/cube meter' + +SELF_COLLISION_ACTIVE = 'Activate self collsion between particles in the system' +OTHER_COLLISION_ACTIVE = 'Activate collision with particles from others systems' +FRICTION = 'Friction between particles at collision 0 = no friction , 1 = full friction' +COLLISION_DAMPING = 'Damping between particles at collision 0 = bouncy , 1 = no collision' +COLLISION_GROUP = 'Choose a collision group you want to collide with' + +LINKS_ACTIVE = 'Activate links between particles of this system' +LINK_OTHER_ACTIVE = '' +LINK_GROUP = '' +LINK_RELATIVE_LENGTH = 'Activate search distance relative to particles radius' +LINK_FRICTION = 'Friction in links , a kind of viscosity. Slow down tangent velocity. 0 = no friction , 1.0 = full friction' +LINK_LENGTH = 'Searching range to make a link between particles' +LINK_TENSION = 'Make link bigger or smaller than it\'s created (1 = normal , 0.9 = 10% smaller , 1.15 = 15% bigger)' +LINK_TENSION_RANDOM = 'Tension random' +LINK_MAX = 'Maximum number of links per particles' +LINK_STIFFNESS = 'Stiffness of links between particles' +LINK_STIFFNESS_RANDOM = 'Random variation for stiffness' +LINK_STIFFNESS_EXPONENT = 'Give an exponent force to the spring links' +LINK_DAMPING = 'Damping effect on spring links' +LINK_DAMPING_RANDOM = 'Random variation on damping' +LINK_BROKEN = 'How much links can stretch before they are broken. 0.01 = 1% , 0.5 = 50% , 2.0 = 200% ...' +LINK_BROKEN_RANDOM = 'Give a random variation to the stretch limit' +LINK_SAME_VALUE = 'When active , expansion and compression of the spring have same value' +LINK_EXPENSION_STIFFNESS = 'Expension stiffness of links between particles' +LINK_EXPENSION_STIFFNESS_RANDOM = 'Random variation for expansion stiffness' +LINK_EXPENSION_STIFFNESS_EXPONENT = 'Give an exponent force to the expension spring links' +LINK_EXPENSION_DAMPING = 'Damping effect on expension spring links' +LINK_EXPENSION_DAMPING_RANDOM = 'Random variation on expension damping' +LINK_EXPENSION_BROKEN = 'How much links can expand before they are broken. 0.01 = 1% , 0.5 = 50% , 2.0 = 200% ...' +LINK_EXPENSION_BROKEN_RANDOM = 'Give a random variation to the expension stretch limit' + +RELINK_GROUP = 'Choose a group that new link are possible' +RELINK_CHANCE = 'Chance of a new link created on collision. 0 = off , 100 = 100% chance' +RELINK_CHANCE_RANDOM = 'Give a random variation to the chance of new link' +RELINK_TENSION = 'Make link bigger or smaller than it\'s created (1 = normal , 0.9 = 10% smaller , 1.15 = 15% bigger)' +RELINK_TENSION_RANDOM = 'Tension random' +RELINK_MAX = 'Maximum number of links per particles' +RELINK_STIFFNESS = 'Stiffness of links between particles' +RELINK_STIFFNESS_RANDOM = 'Random variation for stiffness' +RELINK_STIFFNESS_EXPONENT = 'Give an exponent force to the spring links' +RELINK_DAMPING = 'Damping effect on spring links' +RELINK_DAMPING_RANDOM = 'Random variation on damping' +RELINK_BROKEN = 'How much links can stretch before they are broken. 0.01 = 1% , 0.5 = 50% , 2.0 = 200% ...' +RELINK_BROKEN_RANDOM = 'Give a random variation to the stretch limit' +RELINK_SAME_VALUE = 'When active , expansion and compression of the spring have same value' +RELINK_EXPENSION_STIFFNESS = 'Stiffness of links expension between particles' +RELINK_EXPENSION_STIFFNESS_RANDOM = 'Random variation for expension stiffness' +RELINK_EXPENSION_STIFFNESS_EXPONENT = 'Give an exponent force to the spring links' +RELINK_EXPENSION_DAMPING = 'Damping effect on expension spring links' +RELINK_EXPENSION_DAMPING_RANDOM = 'Random variation on damping' +RELINK_EXPENSION_BROKEN = 'How much link can stretch before they broken. 0.01 = 1% , 0.5 = 50% , 2.0 = 200% ...' +RELINK_EXPENSION_BROKEN_RANDOM = 'Give a random variation to the stretch limit' + +VAR_1 = 'Current number of particles to calculate substep' +VAR_2 = 'Current substep' +VAR_3 = 'Targeted number of particles you want to increase or decrease from current system to calculate substep you need to achieve similar effect' +BAKE_UV = 'Bake uv when finished' + +TIME_SCALE_ACTIVE = 'Activate TimeScaling' +TIME_SCALE = 'SpeedUp or Slow down the simulation with this multiplier' +SUBSTEP = 'mol_substep. Higher is more stable and accurate, but slower' +BAKE = 'Bake simulation when finished' +RENDER = 'Start rendering animation when simulation is finished. WARNING: This will freeze blender until the render is finished.' +CPU = 'Number of cpu\'s included to process the simulation' diff --git a/molecular/molcore.py b/molecular/molcore.py deleted file mode 100644 index 35f0728..0000000 --- a/molecular/molcore.py +++ /dev/null @@ -1,512 +0,0 @@ -from math import floor -from sys import getsizeof -from time import clock -from random import random - -def init(importdata): - global fps - global substep - global psys - global kdtree - global parlist - - fps = importdata[0][0] - substep = importdata[0][1] - psys = [] - for i in importdata[1:]: - psys.append(ParSys(i)) - parnum = 0 - for i in psys: - parnum += i.parnum - parlist = [] - for i in psys: - for ii in i.particle: - parlist.append(ii) - timer = clock() - kdtree = KDTree() - kdtree.create_nodes(len(parlist)) - kdtree.create_tree(parlist,"root") - print(" KDtree generation take:",round(clock()-timer,5),"sec") - """ - point = (0,0,0) - r = 2 - timer = clock() - rquery = kdtree.rnn_query(point,r) - print(" RNN query number find:",len(rquery)) - print(" KDtree RNN query take:",round(clock()-timer,5),"sec") - timer = clock() - bquery = kdtree.brute_query(point,r) - print(" Brute query number find:",len(bquery)) - print(" Brute query take:",round(clock()-timer,5),"sec") - if len(rquery) != len(bquery): - print("> DONT MATCH!!!") - else: - print(" Match!") - """ - for i in psys: - for ii in i.particle: - create_link(ii) - - #for i in kdtree.nodes: - #ppar = i.particle.id - #print(parlist[ppar].id) - return parnum - - -def simulate(importdata): - global kdtree - global parlist - - update_ParSys(importdata) - #print(len(parlist)) - kdtree.create_tree(parlist,"root") - for parsys in psys: - for par in parsys.particle: - collide(par) - solve_link(par) - #print(par.state) - exportdata = [] - parloc = [] - parvel = [] - parloctmp = [] - parveltmp = [] - for parsys in psys: - for par in parsys.particle: - parloctmp += par.loc - parveltmp += par.vel - parloc.append(parloctmp) - parvel.append(parveltmp) - parloctmp = [] - parveltmp = [] - exportdata = [parloc,parvel] - return exportdata - - - -def collide(par): - global kdtree - - neighbours = kdtree.rnn_query(par.loc,par.size * 2) - - for ii in neighbours: - i = ii[0].particle - if i not in par.collided_with: - if i.sys != par.sys : - if i.sys.othercollision_active == False or par.sys.othercollision_active == False: - return - #print(par.sys.collision_group) - if i.sys.collision_group != par.sys.collision_group: - return - if i.sys == par.sys and par.sys.selfcollision_active == False: - return - - stiff = (fps * (substep +1)) - target = (par.size + i.size) * 0.99 - sqtarget = target**2 - - if par.state <= 1 and i.state <= 1 and i not in par.link_with and par not in i.link_with: - lenghtx = par.loc[0] - i.loc[0] - lenghty = par.loc[1] - i.loc[1] - lenghtz = par.loc[2] - i.loc[2] - sqlenght = ii[1] - if sqlenght != 0 and sqlenght < sqtarget: - lenght = sqlenght**0.5 - factor = (lenght - target) / lenght - ratio1 = (i.mass/(par.mass + i.mass)) - ratio2 = (par.mass/(par.mass + i.mass)) - - par.vel[0] -= (lenghtx * factor * ratio1) * stiff - par.vel[1] -= (lenghty * factor * ratio1) * stiff - par.vel[2] -= (lenghtz * factor * ratio1) * stiff - i.vel[0] += (lenghtx * factor * ratio2) * stiff - i.vel[1] += (lenghty * factor * ratio2) * stiff - i.vel[2] += (lenghtz * factor * ratio2) * stiff - - - - col_normal1 = [(i.loc[0] - par.loc[0]) / lenght,(i.loc[1] - par.loc[1]) / lenght,(i.loc[2] - par.loc[2]) / lenght] - #print("i.vel:",i.vel) - #print("Normal",col_normal1) - #print("Normal_length",(col_normal1[0]**2 + col_normal1[1]**2 + col_normal1[2]**2)**0.5) - col_normal2 = [col_normal1[0] * -1,col_normal1[1] * -1,col_normal1[2] * -1] - - factor1 = dot_product(par.vel,col_normal1) - ypar_vel = [factor1 * col_normal1[0],factor1 * col_normal1[1],factor1 * col_normal1[2]] - xpar_vel = [par.vel[0] - ypar_vel[0],par.vel[1] - ypar_vel[1],par.vel[2] - ypar_vel[2]] - - factor2 = dot_product(i.vel,col_normal2) - yi_vel = [factor2 * col_normal2[0],factor2 * col_normal2[1],factor2* col_normal2[2]] - xi_vel = [i.vel[0] - yi_vel[0],i.vel[1] - yi_vel[1],i.vel[2] - yi_vel[2]] - - #print("yi_vel:",yi_vel) - #print("xi_vel:",xi_vel) - - """ - Ua = factor1 - #print("Ua:",Ua) - Ub = -factor2 - #print("Ub:",Ub) - Cr = 1 - Ma = par.mass - Mb = i.mass - Va = (Cr*Mb*(Ub-Ua)+Ma*Ua+Mb*Ub)/(Ma+Mb) - Vb = (Cr*Ma*(Ua-Ub)+Ma*Ua+Mb*Ub)/(Ma+Mb) - #print("Va:",Va) - #print("Vb:",Vb) - - #print("factor:",-(factor * ratio1) * stiff) - mula = 1 - #print("mula:",mula) - mulb = 1 - #print("mulb:",mulb) - ypar_vel[0] = col_normal1[0] * Va * mula - ypar_vel[1] = col_normal1[1] * Va * mula - ypar_vel[2] = col_normal1[2] * Va * mula - yi_vel[0] = col_normal1[0] * Vb * mulb - yi_vel[1] = col_normal1[1] * Vb * mulb - yi_vel[2] = col_normal1[2] * Vb * mulb - #print("yi_vel after:",yi_vel) - #print("xi_vel after:",xi_vel) - """ - friction = 1 - ((par.sys.friction + i.sys.friction ) / 2) - xpar_vel[0] *= friction - xpar_vel[1] *= friction - xpar_vel[2] *= friction - xi_vel[0] *= friction - xi_vel[1] *= friction - xi_vel[2] *= friction - - #print("par_vel befor:",par.vel) - #print("i_vel befor:",i.vel) - par.vel = [ypar_vel[0] + xpar_vel[0],ypar_vel[1] + xpar_vel[1],ypar_vel[2] + xpar_vel[2]] - i.vel = [yi_vel[0] + xi_vel[0],yi_vel[1] + xi_vel[1],yi_vel[2] + xi_vel[2]] - #print("par_vel after:",par.vel) - #print("i_vel after:",i.vel) - - """ - if abs(Va) < abs(((factor * ratio1) * stiff)): - par.vel[0] -= ((lenghtx * factor * ratio1) * stiff) - par.vel[1] -= ((lenghty * factor * ratio1) * stiff) - par.vel[2] -= ((lenghtz * factor * ratio1) * stiff) - if abs(Vb) < abs(((factor * ratio2) * stiff)): - i.vel[0] += ((lenghtx * factor * ratio2) * stiff) - i.vel[1] += ((lenghty * factor * ratio2) * stiff) - i.vel[2] += ((lenghtz * factor * ratio2) * stiff) - """ - - i.collided_with.append(par) - if (par.sys.relink_chance + par.sys.relink_chance / 2) > 0: - create_link(par,[i,lenght**2]) - - - -def solve_link(par): - broken_links = [] - for link in par.links: - - stiff = link.stiffness * (fps * (substep +1)) - damping = link.damping - timestep = 1/(fps * (substep +1)) - exp = link.exponent - par1 = link.start - par2 = link.end - Loc1 = par1.loc - Loc2 = par2.loc - V1 = link.start.vel - V2 = link.end.vel - LengthX = Loc2[0] - Loc1[0] - LengthY = Loc2[1] - Loc1[1] - LengthZ = Loc2[2] - Loc1[2] - Length = (LengthX**2 + LengthY**2 + LengthZ**2)**(0.5) - if link.lenght != Length: - Vx = V2[0] - V1[0] - Vy = V2[1] - V1[1] - Vz = V2[2] - V1[2] - V = (Vx * LengthX + Vy * LengthY+Vz * LengthZ) / Length - ForceSpring = ((Length - link.lenght)**(exp)) * stiff - ForceDamper = damping * V - ForceX = (ForceSpring + ForceDamper) * LengthX / Length - ForceY = (ForceSpring + ForceDamper) * LengthY / Length - ForceZ = (ForceSpring + ForceDamper) * LengthZ / Length - Force1 = [ForceX,ForceY,ForceZ] - Force2 = [-ForceX,-ForceY,-ForceZ] - ratio1 = (par2.mass/(par1.mass + par2.mass)) - ratio2 = (par1.mass/(par1.mass + par2.mass)) - par1.vel[0] += Force1[0] * ratio1 - par1.vel[1] += Force1[1] * ratio1 - par1.vel[2] += Force1[2] * ratio1 - par2.vel[0] += Force2[0] * ratio2 - par2.vel[1] += Force2[1] * ratio2 - par2.vel[2] += Force2[2] * ratio2 - if Length > (link.lenght * (1 + link.broken)) or Length < (link.lenght * (1 - link.broken)): - #print("broke!!!!!") - broken_links.append(link) - if par2 in par1.link_with: - par1.link_with.remove(par2) - if par1 in par2.link_with: - par2.link_with.remove(par1) - - par.links = list(set(par.links) - set(broken_links)) - - - - - -def update_ParSys(data): - global psys - global substep - global parlist - i = 0 - ii = 0 - #print(data[1][2]) - parlist = [] - for parsys in psys: - for par in parsys.particle: - par.loc = data[i][0][(ii * 3):(ii * 3 + 3)] - par.vel = data[i][1][(ii * 3):(ii * 3 + 3)] - if par.state == 0 and data[i][2][ii] == 0: - par.state = data[i][2][ii] + 1 - create_link(par) - parlist.append(par) - elif par.state == 1 and data[i][2][ii] == 0: - par.state = 1 - parlist.append(par) - else: - par.state = data[i][2][ii] - par.collided_with = [] - #print("state:",par.state) - ii += 1 - ii = 0 - i += 1 - -def create_link(par,parothers = None): - global kdtree - if par.sys.links_active == 0: - return - if parothers == None: - neighbours = kdtree.rnn_query(par.loc,par.sys.link_length) - else: - neighbours = [parothers] - - for ii in neighbours: - if parothers == None: - i = ii[0].particle - else: - i = ii[0] - if par != i: - if par not in i.link_with and i.state <= 1 and par.state <= 1: - link = links() - link.lenght = ii[1]**0.5 - link.start = par - link.end = i - if parothers == None: - stiffrandom = (par.sys.link_stiffrand + i.sys.link_stiffrand) / 2 * 2 - link.stiffness = ((par.sys.link_stiff + i.sys.link_stiff)/2) * (((random() * stiffrandom) - (stiffrandom / 2)) + 1) - link.exponent = abs((par.sys.link_stiffexp + i.sys.link_stiffexp) / 2) - damprandom = ((par.sys.link_damprand + i.sys.link_damprand) / 2) * 2 - link.damping = ((par.sys.link_damp + i.sys.link_damp) / 2) * (((random() * damprandom) - (damprandom / 2)) + 1) - brokrandom = ((par.sys.link_brokenrand + i.sys.link_brokenrand) / 2) * 2 - link.broken = ((par.sys.link_broken + i.sys.link_broken) / 2) * (((random() * brokrandom) - (brokrandom / 2)) + 1) - par.links.append(link) - par.link_with.append(i) - i.link_with.append(par) - del link - if parothers != None and par.sys.relink_group == i.sys.relink_group: - relinkrandom = random() - chancerdom = (par.sys.relink_chancerand + i.sys.relink_chancerand) / 2 * 2 - if relinkrandom <= ((par.sys.relink_chance + i.sys.relink_chance) / 2) * (((random() * chancerdom) - (chancerdom / 2)) + 1): - stiffrandom = (par.sys.relink_stiffrand + i.sys.relink_stiffrand) / 2 * 2 - link.stiffness = ((par.sys.relink_stiff + i.sys.relink_stiff)/2) * (((random() * stiffrandom) - (stiffrandom / 2)) + 1) - link.exponent = abs((par.sys.relink_stiffexp + i.sys.relink_stiffexp) / 2) - damprandom = ((par.sys.relink_damprand + i.sys.relink_damprand) / 2) * 2 - link.damping = ((par.sys.relink_damp + i.sys.relink_damp) / 2) * (((random() * damprandom) - (damprandom / 2)) + 1) - brokrandom = ((par.sys.relink_brokenrand + i.sys.relink_brokenrand) / 2) * 2 - link.broken = ((par.sys.relink_broken + i.sys.relink_broken) / 2) * (((random() * brokrandom) - (brokrandom / 2)) + 1) - par.links.append(link) - par.link_with.append(i) - i.link_with.append(par) - del link - - - #print("num link",len(par.links)) - - -class links: - def __init__(self): - self.lenght = "lenght of the link" - self.start = "first particle starting from" - self.end = "second particle ending with" - self.stiffness = "stiffness of the link" - self.exponent = "exponent of the stiffness" - self.damping = "damping of the link" - self.broken = "max stretch factor to link broken" - -class ParSys: - def __init__(self,data): - global hashgrid - - self.parnum = data[0] - self.particle = [0] * self.parnum - self.sqsize_list = [] - self.selfcollision_active = data[6][0] - self.othercollision_active = data[6][1] - self.collision_group = data[6][2] - self.links_active = data[6][3] - self.link_length = data[6][4] - self.link_stiff = data[6][5] - self.link_stiffrand = data[6][6] - self.link_stiffexp = data[6][7] - self.friction = data[6][8] - self.link_damp = data[6][9] - self.link_damprand = data[6][10] - self.link_broken = data[6][11] - self.link_brokenrand = data[6][12] - self.relink_group = data[6][13] - self.relink_chance = data[6][14] - self.relink_chancerand = data[6][15] - self.relink_stiff = data[6][16] - self.relink_stiffexp = data[6][17] - self.relink_stiffrand = data[6][18] - self.relink_damp = data[6][19] - self.relink_damprand = data[6][20] - self.relink_broken = data[6][21] - self.relink_brokenrand = data[6][22] - - for i in range(self.parnum): - self.particle[i] = Particle() - self.particle[i].id = i - self.particle[i].loc = data[1][(i * 3):(i * 3 + 3)] - self.particle[i].vel = data[2][(i * 3):(i * 3 + 3)] - self.particle[i].size = data[3][i] - self.particle[i].sqsize = sq_number(self.particle[i].size) - if self.particle[i].sqsize not in self.sqsize_list: - self.sqsize_list.append(self.particle[i].sqsize) - self.particle[i].mass = data[4][i] - self.particle[i].state = data[5][i] - self.particle[i].sys = self - #print(self.particle[i].size,self.particle[i].sqsize) - - -class Particle(ParSys): - def __init__(self): - self.id = 0 - self.loc = [0,0,0] - self.vel = [0,0,0] - self.size = "is size" - self.sqsize = "near square number for hashing" - self.mass = "is mass" - self.state = "is alive state" - self.sys = "is parent system" - self.collided_with = [] - self.links = [] - self.link_with = [] - -class KDTree: - root_node = "node on top" - nodes = [] - result = [] - index = 0 - def create_nodes(self,parnum): - if self.nodes == []: - for i in range(parnum): - node = Nodes() - self.nodes.append(node) - self.index = 0 - def create_tree(self,parlist,name,depth = 0): - if not parlist: - return None - - k = len(parlist[0].loc) - axis = depth % k - parlist.sort(key= lambda p: p.loc[axis]) - median = int(len(parlist) / 2) - if depth == 0: - self.index = 0 - node = self.nodes[self.index] - node.name = name - if parlist and depth == 0: - self.root_node = node - self.index += 1 - node.particle = parlist[median] - node.left_child = self.create_tree(parlist[:median],"left" + str(depth+1),depth + 1) - node.right_child = self.create_tree(parlist[median + 1:],"right" + str(depth+1),depth + 1) - return node - - def rnn_query(self,point,dist): - self.result = [] - if self.root_node == None: - return [] - else: - sqdist = dist**2 - #print(" distance to reach:",dist) - k = len(self.root_node.particle.loc) - self.rnn_search(self.root_node,point,dist,sqdist,k) - - return self.result - - def rnn_search(self,node,point,dist,sqdist,k,depth = 0): - - if node == None: - return None - - axis = depth % k - #print(" " + node.name + "(" + str(node.particle.id) + ")" + " viewed at " + str(square_dist(point,node.particle.loc,k)**0.5)) - - if (point[axis] - node.particle.loc[axis])**2 <= sqdist: - realdist = square_dist(point,node.particle.loc,k) - if realdist <= sqdist: - #print(" " + node.name + "(" + str(node.particle.id) + ")" + " added") - self.result.append((node,realdist)) - self.rnn_search(node.left_child,point,dist,sqdist,k,depth + 1) - self.rnn_search(node.right_child,point,dist,sqdist,k,depth + 1) - - else: - if point[axis] <= node.particle.loc[axis]: - self.rnn_search(node.left_child,point,dist,sqdist,k,depth + 1) - if point[axis] >= node.particle.loc[axis]: - self.rnn_search(node.right_child,point,dist,sqdist,k,depth + 1) - - - def brute_query(self,point,dist): - self.result = [] - k = len(point) - dist = dist**2 - for i in self.nodes: - #print(" " + i.name + "(" + str(i.particle.id) + ")" + " viewed at " + str(square_dist(point,i.particle.loc,k)**0.5)) - if square_dist(i.particle.loc,point,k) <= dist: - #print(" " + i.name + "(" + str(i.particle.id) + ")" + " added") - self.result.append(i) - return self.result - - - -class Nodes: - def __init__(self): - name = "node name" - loc = [0,0,0] - particle = "particle" - self.left_child = None - self.right_child = None - - -def sq_number(val): - nearsq = 8 - while val > nearsq or val < nearsq / 2: - if val > nearsq: - nearsq = nearsq * 2 - elif val < nearsq / 2: - nearsq = nearsq / 2 - return nearsq - -def square_dist(point1,point2,k): - sq_dist = 0 - for i in range(k): - sq_dist += (point1[i] - point2[i])**2 - return sq_dist - -def cross_product(u,v): - cross = [(u[1] * v[2] - u[2] * v[1]),(u[2] * v[0] - u[0] * v[2]),(u[0] * v[1] - u[1] * v[0])] - return cross -def dot_product(u,v): - dot = (u[0] * v[0]) + (u[1] * v[1]) + (u[2] * v[2]) - return dot diff --git a/molecular/names.py b/molecular/names.py new file mode 100644 index 0000000..c1582d0 --- /dev/null +++ b/molecular/names.py @@ -0,0 +1,19 @@ +# Labels text +DENSITY = 'Density' +COLLISION = 'Collision' +LINKS = 'Links' +INITIAL_LINKING = 'Initial Linking (at birth)' +NEW_LINKING = 'New Linking (at collision)' +UVS = 'UV\'s' +SIMULATE = 'Simulate' +CPU_USED = 'CPU used' +MOLECULAR_TOOLS = 'Tools' +PARTICLE_UV = 'Particle UV' +SET_POSITION = 'Set current particles position' +UV_HELP = 'Has global or current uv in angular velocity' +CYCLES_HELP = 'Retrieve it with Cycles particle info node' +SUBSTEPS_CALCULATOR = 'Substeps Calculator' +THANKS = 'THANKS TO ALL DONATORS!' +SUPPORT_WORK = 'If you want donate to support my work' +VISIT = 'or visit:' +SITE = 'www.pyroevil.com/donate/' diff --git a/molecular/operators.py b/molecular/operators.py new file mode 100644 index 0000000..421d50d --- /dev/null +++ b/molecular/operators.py @@ -0,0 +1,333 @@ +from time import perf_counter as clock, sleep, strftime, gmtime, time + +import bpy +from mathutils import Vector +from mathutils.geometry import barycentric_transform as barycentric + +from . import simulate, core +from .utils import get_object, destroy_caches + + +class MolSimulate(bpy.types.Operator): + bl_idname = "object.mol_simulate" + bl_label = 'Simulate' + + def execute(self, context): + for ob in bpy.data.objects: + destroy_caches(ob) + + print('Molecular Sim Start' + '-' * 50) + mol_stime = clock() + scene = context.scene + scene.mol_simrun = True + scene.mol_minsize = 1000000000.0 + scene.mol_newlink = 0 + scene.mol_deadlink = 0 + scene.mol_totallink = 0 + scene.mol_totaldeadlink = 0 + scene.mol_timeremain = "...Simulating..." + scene.frame_set(frame=scene.frame_start) + scene.mol_old_endframe = scene.frame_end + mol_substep = scene.mol_substep + scene.render.frame_map_old = 1 + scene.render.frame_map_new = mol_substep + 1 + scene.frame_end *= mol_substep + 1 + + if scene.mol_timescale_active == True: + fps = scene.render.fps * scene.timescale + else: + fps = scene.render.fps + + cpu = scene.mol_cpu + mol_exportdata = context.scene.mol_exportdata + mol_exportdata.clear() + mol_exportdata.append([fps, mol_substep, 0, 0, cpu]) + mol_stime = clock() + simulate.pack_data(context, True) + etime = clock() + print(" PackData take " + str(round(etime - mol_stime, 3)) + "sec") + mol_stime = clock() + mol_report = core.init(mol_exportdata) + etime = clock() + print(" Export time take " + str(round(etime - mol_stime, 3)) + "sec") + print(" total numbers of particles: " + str(mol_report)) + print(" start processing:") + bpy.ops.wm.mol_simulate_modal() + return {'FINISHED'} + + +class MolSetGlobalUV(bpy.types.Operator): + bl_idname = "object.mol_set_global_uv" + bl_label = "Mol Set UV" + + def execute(self, context): + scene = context.scene + obj = get_object(context, context.object) + + psys = obj.particle_systems.active + coord = [0, 0, 0] * len(psys.particles) + psys.particles.foreach_get("location", coord) + psys.particles.foreach_set("angular_velocity", coord) + + return {'FINISHED'} + + +class MolSetActiveUV(bpy.types.Operator): + bl_idname = "object.mol_set_active_uv" + bl_label = "Mol Set Active UV" + + def execute(self, context): + scene = context.scene + + #dont use rotation data here from cache, only for render mode + psys_orig = context.object.particle_systems.active + psys_orig.settings.use_rotations = False + + obj = get_object(context, context.object) + + scene.mol_objuvbake = obj.name + scene.mol_psysuvbake = obj.particle_systems.active.name + + if not obj.data.uv_layers.active: + return {'FINISHED'} + + print(' start bake uv from:', obj.name) + + # take the original object as source for copying the data, + # else the materials are not copied properly and context.view_layer.update() crashes + obdata = context.object.data.copy() + obj2 = bpy.data.objects.new(name="mol_uv_temp", object_data=obdata) + obj2.matrix_world = obj.matrix_world + + context.scene.collection.objects.link(obj2) + mod = obj2.modifiers.new("tri_for_uv", "TRIANGULATE") + mod.ngon_method = 'BEAUTY' + mod.quad_method = 'BEAUTY' + + ctx = bpy.context.copy() + ctx["object"] = obj2 + with context.temp_override(**ctx): + bpy.ops.object.modifier_apply(modifier=mod.name) + + context.view_layer.update() + + psys = obj.particle_systems[scene.mol_psysuvbake] + par_uv = [] + me = obj2.data + + for par in psys.particles: + + parloc = (par.location @ obj2.matrix_world) - obj2.location + + point = obj2.closest_point_on_mesh(parloc) + vindex1 = me.polygons[point[3]].vertices[0] + vindex2 = me.polygons[point[3]].vertices[1] + vindex3 = me.polygons[point[3]].vertices[2] + + v1 = (obj2.matrix_world @ me.vertices[vindex1].co).to_tuple() + v2 = (obj2.matrix_world @ me.vertices[vindex2].co).to_tuple() + v3 = (obj2.matrix_world @ me.vertices[vindex3].co).to_tuple() + + uvindex1 = me.polygons[point[3]].loop_start + 0 + uvindex2 = me.polygons[point[3]].loop_start + 1 + uvindex3 = me.polygons[point[3]].loop_start + 2 + uv1 = me.uv_layers.active.data[uvindex1].uv.to_3d() + uv2 = me.uv_layers.active.data[uvindex2].uv.to_3d() + uv3 = me.uv_layers.active.data[uvindex3].uv.to_3d() + + p = obj2.matrix_world @ point[1] + + v1 = Vector(v1) + v2 = Vector(v2) + v3 = Vector(v3) + uv1 = Vector(uv1) + uv2 = Vector(uv2) + uv3 = Vector(uv3) + newuv = barycentric(p, v1, v2, v3, uv1, uv2, uv3) + + parloc = par.location @ obj2.matrix_world + + dist = (Vector(( + parloc[0] - p[0], + parloc[1] - p[1], + parloc[2] - p[2] + ))).length + + newuv[2] = dist + newuv = newuv.to_tuple() + par.angular_velocity = newuv + par_uv.append(newuv) + + scene.collection.objects.unlink(obj2) + bpy.data.objects.remove(obj2) + bpy.data.meshes.remove(obdata) + print(' uv baked on:', psys.settings.name) + context.object["par_uv"] = par_uv + + return {'FINISHED'} + + +def convert_time_to_string(total_time): + HOUR_IN_SECONDS = 60 * 60 + MINUTE_IN_SCEONDS = 60 + time_string = '' + if total_time > 10.0: + total_time = int(total_time) + if total_time > MINUTE_IN_SCEONDS and total_time <= HOUR_IN_SECONDS: + minutes = total_time // MINUTE_IN_SCEONDS + seconds = total_time - minutes * MINUTE_IN_SCEONDS + time_string = '{0} min {1} sec'.format(minutes, seconds) + elif total_time <= MINUTE_IN_SCEONDS: + time_string = '{0} seconds'.format(total_time) + elif total_time > HOUR_IN_SECONDS: + hours = total_time // HOUR_IN_SECONDS + minutes = total_time - (total_time // HOUR_IN_SECONDS) * HOUR_IN_SECONDS + time_string = '{0} hours {1} min'.format(hours, minutes) + else: + seconds = round(total_time, 2) + time_string = '{0} seconds'.format(seconds) + return time_string + + +class MolSimulateModal(bpy.types.Operator): + """Operator which runs its self from a timer""" + bl_idname = "wm.mol_simulate_modal" + bl_label = "Simulate Molecular" + _timer = None + + def check_write_uv_cache(self, context): + for ob in bpy.data.objects: + obj = get_object(context, ob) + + for psys in obj.particle_systems: + + # prepare object as in "open" the cache for angular velocity data + if context.scene.frame_current == context.scene.frame_start: + psys.settings.use_rotations = True + psys.settings.angular_velocity_mode = 'RAND' + + if psys.settings.mol_bakeuv and "par_uv" in ob: + par_uv = ob["par_uv"] + print("Writing UV data...") + for k, par in enumerate(psys.particles): + par.angular_velocity = par_uv[k] + + def check_bake_uv(self, context): + # bake the UV in the beginning, and store coordinates in custom property + scene = context.scene + frame_old = scene.frame_current + + for ob in bpy.data.objects: + obj = get_object(context, ob) + + for psys in obj.particle_systems: + if psys.settings.mol_bakeuv: + + scene.mol_objuvbake = obj.name + context.view_layer.update() + # cast float value psys.settings.frame_start to int here because frame_set accepts only ints now. + scene.frame_set(frame=int(psys.settings.frame_start)) + context.view_layer.update() + + bpy.ops.object.mol_set_active_uv() + + scene.frame_set(frame=frame_old) + + def modal(self, context, event): + scene = context.scene + frame_end = scene.frame_end + frame_current = scene.frame_current + if event.type == 'ESC' or frame_current == frame_end: + if scene.mol_bake: + fake_context = context.copy() + for ob in bpy.data.objects: + obj = get_object(context, ob) + for psys in obj.particle_systems: + if psys.settings.mol_active and len(psys.particles): + fake_context["point_cache"] = psys.point_cache + with context.temp_override(**fake_context): + bpy.ops.ptcache.bake_from_cache() + scene.render.frame_map_new = 1 + scene.frame_end = scene.mol_old_endframe + context.view_layer.update() + + # self.check_bake_uv(context) + + if frame_current == frame_end and scene.mol_render: + bpy.ops.render.render(animation=True) + + scene.frame_set(frame=scene.frame_start) + + core.memfree() + scene.mol_simrun = False + mol_exportdata = scene.mol_exportdata + mol_exportdata.clear() + print('-' * 50 + 'Molecular Sim end') + # total time + tt = time() - self.st + # total time string + tt_s = convert_time_to_string(tt) + self.report({'INFO'}, 'Total time: {0}'.format(tt_s)) + return self.cancel(context) + + if event.type == 'TIMER': + if frame_current == scene.frame_start: + scene.mol_stime = clock() + mol_exportdata = context.scene.mol_exportdata + mol_exportdata.clear() + simulate.pack_data(context, False) + mol_importdata = core.simulate(mol_exportdata) + + i = 0 + for ob in bpy.data.objects: + obj = get_object(context, ob) + + for psys in obj.particle_systems: + if psys.settings.mol_active and len(psys.particles): + psys.particles.foreach_set('velocity', mol_importdata[1][i]) + i += 1 + + mol_substep = scene.mol_substep + framesubstep = frame_current / (mol_substep + 1) + if framesubstep == int(framesubstep): + etime = clock() + print(" frame " + str(framesubstep + 1) + ":") + print(" links created:", scene.mol_newlink) + if scene.mol_totallink: + print(" links broked :", scene.mol_deadlink) + print(" total links:", scene.mol_totallink - scene.mol_totaldeadlink ,"/", scene.mol_totallink," (",round((((scene.mol_totallink - scene.mol_totaldeadlink) / scene.mol_totallink) * 100), 2), "%)") + print(" Molecular Script: " + str(round(etime - scene.mol_stime, 3)) + " sec") + remain = (((etime - scene.mol_stime) * (scene.mol_old_endframe - framesubstep - 1))) + days = int(strftime('%d', gmtime(remain))) - 1 + scene.mol_timeremain = strftime(str(days) + ' days %H hours %M mins %S secs', gmtime(remain)) + print(" Remaining estimated:", scene.mol_timeremain) + scene.mol_newlink = 0 + scene.mol_deadlink = 0 + scene.mol_stime = clock() + stime2 = clock() + scene.mol_newlink += mol_importdata[2] + scene.mol_deadlink += mol_importdata[3] + scene.mol_totallink = mol_importdata[4] + scene.mol_totaldeadlink = mol_importdata[5] + + self.check_write_uv_cache(context) + scene.frame_set(frame=frame_current + 1) + + if framesubstep == int(framesubstep): + etime2 = clock() + print(" Blender: " + str(round(etime2 - stime2, 3)) + " sec") + stime2 = clock() + + return {'PASS_THROUGH'} + + def execute(self, context): + # start time + self.st = time() + self.check_bake_uv(context) + self._timer = context.window_manager.event_timer_add(0.000000001, window=context.window) + context.window_manager.modal_handler_add(self) + return {'RUNNING_MODAL'} + + def cancel(self, context): + context.window_manager.event_timer_remove(self._timer) + return {'CANCELLED'} diff --git a/molecular/properties.py b/molecular/properties.py new file mode 100644 index 0000000..8d57ab7 --- /dev/null +++ b/molecular/properties.py @@ -0,0 +1,350 @@ +import multiprocessing + +import bpy + +from . import descriptions + + +def define_props(): + parset = bpy.types.ParticleSettings + + parset.mol_active = bpy.props.BoolProperty( + name="mol_active", description=descriptions.ACTIVE, default=False + ) + parset.mol_refresh = bpy.props.BoolProperty( + name="mol_refresh", description=descriptions.REFRESH, default=True + ) + parset.mol_density_active = bpy.props.BoolProperty( + name="Calculate particles weight by density", + description=descriptions.DENSITY_ACTIVE, + default=False + ) + + matter_items = [ + ("-1", "custom", descriptions.MATTER_CUSTOM), + ("1555", "sand", descriptions.MATTER_SAND), + ("1000", "water", descriptions.MATTER_WATER), + ("7800", "iron", descriptions.MATTER_IRON) + ] + + parset.mol_matter = bpy.props.EnumProperty( + name='Preset', + items=matter_items, + description=descriptions.MATTER + ) + parset.mol_density = bpy.props.FloatProperty( + name="Kg per CubeMeter:", description=descriptions.DENSITY, + default=1000, min=0.001 + ) + + parset.mol_selfcollision_active = bpy.props.BoolProperty( + name="Activate Self Collision", + description=descriptions.SELF_COLLISION_ACTIVE, + default=False + ) + parset.mol_othercollision_active = bpy.props.BoolProperty( + name="Activate Collision with others", + description=descriptions.OTHER_COLLISION_ACTIVE, + default=False + ) + parset.mol_friction = bpy.props.FloatProperty( + name='Friction:', description=descriptions.FRICTION, + default=0.005, min=0, max=1, precision=6, subtype='FACTOR' + ) + parset.mol_collision_damp = bpy.props.FloatProperty( + name="Damping:", description=descriptions.COLLISION_DAMPING, + default=0.005, min=0, max=1, precision=6, subtype='FACTOR' + ) + + parset.mol_collision_group = bpy.props.IntProperty( + name='Collide only with:', default=1, min=1, + description=descriptions.COLLISION_GROUP + ) + + parset.mol_links_active = bpy.props.BoolProperty( + name="Activate Particles linking", + description=descriptions.LINKS_ACTIVE, + default=False + ) + parset.mol_other_link_active = bpy.props.BoolProperty( + name="Activate Particles linking with Others", + description=descriptions.LINK_OTHER_ACTIVE, default=False + ) + + parset.mol_link_group = bpy.props.IntProperty( + name='Linking only with:', default=1, min=1, + description=descriptions.LINK_GROUP + ) + + parset.mol_link_rellength = bpy.props.BoolProperty( + name="Relative", + description=descriptions.LINK_RELATIVE_LENGTH, + default=True + ) + parset.mol_link_friction = bpy.props.FloatProperty( + name="Link friction", description=descriptions.LINK_FRICTION, + min=0, max=1, default=0.005, precision=6, subtype='FACTOR' + ) + parset.mol_link_length = bpy.props.FloatProperty( + name="Search Length", description=descriptions.LINK_LENGTH, + min=0, precision=6, default=1 + ) + parset.mol_link_tension = bpy.props.FloatProperty( + name="Tension", description=descriptions.LINK_TENSION, + min=0, precision=6, default=1 + ) + parset.mol_link_tensionrand = bpy.props.FloatProperty( + name="Rand Tension", + description=descriptions.LINK_TENSION_RANDOM, + min=0, max=1, precision=6, default=0, subtype='FACTOR' + ) + parset.mol_link_max = bpy.props.IntProperty( + name="Max links", description=descriptions.LINK_MAX, + min=0, default=16 + ) + parset.mol_link_stiff = bpy.props.FloatProperty( + name="Stiff", description=descriptions.LINK_STIFFNESS, + min=0, max=1, default=1, precision=6, subtype='FACTOR' + ) + parset.mol_link_stiffrand = bpy.props.FloatProperty( + name="Rand Stiff", + description = descriptions.LINK_STIFFNESS_RANDOM, + min=0, max=1, default=0, precision=6, subtype='FACTOR' + ) + parset.mol_link_stiffexp = bpy.props.IntProperty( + name="Exponent", + description=descriptions.LINK_STIFFNESS_EXPONENT, + default=1, min=1, max=10 + ) + parset.mol_link_damp = bpy.props.FloatProperty( + name="Damping", description=descriptions.LINK_DAMPING, + min=0, max=1, default=1, precision=6, subtype='FACTOR' + ) + parset.mol_link_damprand = bpy.props.FloatProperty( + name="Rand Damping", description=descriptions.LINK_DAMPING_RANDOM, + min=0, max=1, default=0, precision=6, subtype='FACTOR' + ) + parset.mol_link_broken = bpy.props.FloatProperty( + name="Broken", description=descriptions.LINK_BROKEN, + min=0, default=0.5, precision=6 + ) + parset.mol_link_brokenrand = bpy.props.FloatProperty( + name="Rand Broken", + description=descriptions.LINK_BROKEN_RANDOM, + min=0, max=1, default=0, precision=6, subtype='FACTOR' + ) + + parset.mol_link_samevalue = bpy.props.BoolProperty( + name="Same values for compression/expansion", + description=descriptions.LINK_SAME_VALUE, + default=True + ) + + parset.mol_link_estiff = bpy.props.FloatProperty( + name="E Stiff", + description=descriptions.LINK_EXPENSION_STIFFNESS, + min=0, max=1, default=1, precision=6, subtype='FACTOR' + ) + parset.mol_link_estiffrand = bpy.props.FloatProperty( + name="Rand E Stiff", + description=descriptions.LINK_EXPENSION_STIFFNESS_RANDOM, + min=0, max=1, default=0, precision=6, subtype='FACTOR' + ) + parset.mol_link_estiffexp = bpy.props.IntProperty( + name="E Exponent", + description=descriptions.LINK_EXPENSION_STIFFNESS_EXPONENT, + default=1, min=1, max=10 + ) + parset.mol_link_edamp = bpy.props.FloatProperty( + name="E Damping", description=descriptions.LINK_EXPENSION_DAMPING, + min=0, max=1, default=1, precision=6, subtype='FACTOR' + ) + parset.mol_link_edamprand = bpy.props.FloatProperty( + name="Rand E Damping", + description=descriptions.LINK_EXPENSION_DAMPING_RANDOM, + min=0, max=1, default=0, precision=6, subtype='FACTOR' + ) + parset.mol_link_ebroken = bpy.props.FloatProperty( + name="E Broken", + description=descriptions.LINK_EXPENSION_BROKEN, + min=0, default=0.5, precision=6 + ) + parset.mol_link_ebrokenrand = bpy.props.FloatProperty( + name="Rand E Broken", + description=descriptions.LINK_EXPENSION_BROKEN_RANDOM, + min=0, max=1, default=0, precision=6, subtype='FACTOR' + ) + + parset.mol_relink_group = bpy.props.IntProperty( + name='Only links with:', + default=1, min=1, description=descriptions.RELINK_GROUP + ) + + parset.mol_relink_chance = bpy.props.FloatProperty( + name="% Linking", + description=descriptions.RELINK_CHANCE, + min=0, max=100, default=0, precision=6 + ) + parset.mol_relink_chancerand = bpy.props.FloatProperty( + name="Rand % Linking", + description=descriptions.RELINK_CHANCE_RANDOM, + default=0, min=0, max=1, precision=6, subtype='FACTOR' + ) + parset.mol_relink_tension = bpy.props.FloatProperty( + name="Tension", + description=descriptions.RELINK_TENSION, + min=0, precision=6, default=1 + ) + parset.mol_relink_tensionrand = bpy.props.FloatProperty( + name="Rand Tension", + description=descriptions.RELINK_TENSION_RANDOM, + min=0, max=1, default=0, precision=6, subtype='FACTOR' + ) + parset.mol_relink_max = bpy.props.IntProperty( + name="Max links", + description=descriptions.RELINK_MAX, + min=0, default=16 + ) + parset.mol_relink_stiff = bpy.props.FloatProperty( + name="Stiff", + description=descriptions.RELINK_STIFFNESS, + min=0, max=1, default=1, precision=6, subtype='FACTOR' + ) + parset.mol_relink_stiffrand = bpy.props.FloatProperty( + name="Rand Stiff", + description=descriptions.RELINK_STIFFNESS_RANDOM, + min=0, max=1, default=0, precision=6, subtype='FACTOR' + ) + parset.mol_relink_stiffexp = bpy.props.IntProperty( + name="Exponent", + description=descriptions.RELINK_STIFFNESS_EXPONENT, + min=1, max=10, default=1 + ) + parset.mol_relink_damp = bpy.props.FloatProperty( + name="Damping", + description=descriptions.RELINK_DAMPING, + min=0, max=1, default=1, precision=6, subtype='FACTOR' + ) + parset.mol_relink_damprand = bpy.props.FloatProperty( + name="Rand Damping", + description=descriptions.RELINK_DAMPING_RANDOM, + min=0, max=1, default=0, precision=6, subtype='FACTOR' + ) + parset.mol_relink_broken = bpy.props.FloatProperty( + name="Broken", + description=descriptions.RELINK_BROKEN, + min=0, default=0.5, precision=6 + ) + parset.mol_relink_brokenrand = bpy.props.FloatProperty( + name="Rand Broken", + description=descriptions.RELINK_BROKEN_RANDOM, + min=0, max=1, default=0, precision=6, subtype='FACTOR' + ) + + parset.mol_relink_samevalue = bpy.props.BoolProperty( + name="Same values for compression/expansion", + description=descriptions.RELINK_SAME_VALUE, + default=True + ) + + parset.mol_relink_estiff = bpy.props.FloatProperty( + name="E Stiff", + description=descriptions.RELINK_EXPENSION_STIFFNESS, + min=0, max=1, default=1, precision=6, subtype='FACTOR' + ) + parset.mol_relink_estiffrand = bpy.props.FloatProperty( + name="Rand E Stiff", + description=descriptions.RELINK_EXPENSION_STIFFNESS_RANDOM, + min=0, max=1, default=0, precision=6, subtype='FACTOR' + ) + parset.mol_relink_estiffexp = bpy.props.IntProperty( + name="Exponent", + description=descriptions.RELINK_EXPENSION_STIFFNESS_EXPONENT, + min=1, max=10, default=1 + ) + parset.mol_relink_edamp = bpy.props.FloatProperty( + name="E Damping", + description=descriptions.RELINK_EXPENSION_DAMPING, + min=0, max=1, default=1, precision=6, subtype='FACTOR' + ) + parset.mol_relink_edamprand = bpy.props.FloatProperty( + name="Rand E Damping", + description=descriptions.RELINK_EXPENSION_DAMPING_RANDOM, + min=0, max=1, default=0, precision=6, subtype='FACTOR' + ) + parset.mol_relink_ebroken = bpy.props.FloatProperty( + name="E Broken", + description=descriptions.RELINK_EXPENSION_BROKEN, + min=0, default=0.5, precision=6 + ) + parset.mol_relink_ebrokenrand = bpy.props.FloatProperty( + name="Rand E Broken", + description=descriptions.RELINK_EXPENSION_BROKEN_RANDOM, + min=0, max=1, default=0, precision=6, subtype='FACTOR' + ) + + parset.mol_var1 = bpy.props.IntProperty( + name="Current numbers of particles", + description=descriptions.VAR_1, + min=1, default=1000 + ) + parset.mol_var2 = bpy.props.IntProperty( + name="Current substep", + description=descriptions.VAR_2, + min=1, default=4 + ) + parset.mol_var3=bpy.props.IntProperty( + name="Targeted numbers of particles", + description=descriptions.VAR_3, + min=1, default=1000 + ) + parset.mol_bakeuv = bpy.props.BoolProperty( + name="mol_bakeuv", + description=descriptions.BAKE_UV, + default=False + ) + + bpy.types.Scene.mol_timescale_active = bpy.props.BoolProperty( + name="mol_timescale_active", + description=descriptions.TIME_SCALE_ACTIVE, + default=False + ) + bpy.types.Scene.timescale = bpy.props.FloatProperty( + name="timescale", + description=descriptions.TIME_SCALE, + default=1 + ) + bpy.types.Scene.mol_substep = bpy.props.IntProperty( + name="Substeps", + description=descriptions.SUBSTEP, + min=0, max=900, default=4 + ) + bpy.types.Scene.mol_bake = bpy.props.BoolProperty( + name="Bake all at ending", + description=descriptions.BAKE, + default=True + ) + bpy.types.Scene.mol_render = bpy.props.BoolProperty( + name="Render at ending", + description=descriptions.RENDER, + default=False + ) + bpy.types.Scene.mol_cpu = bpy.props.IntProperty( + name="CPU", + description=descriptions.CPU, + default=multiprocessing.cpu_count(), + min=1, max =multiprocessing.cpu_count() + ) + + bpy.types.Scene.mol_exportdata = [] + bpy.types.Scene.mol_minsize = bpy.props.FloatProperty() + bpy.types.Scene.mol_simrun = bpy.props.BoolProperty(default=False) + bpy.types.Scene.mol_timeremain = bpy.props.StringProperty() + bpy.types.Scene.mol_old_endframe = bpy.props.IntProperty() + bpy.types.Scene.mol_newlink = bpy.props.IntProperty() + bpy.types.Scene.mol_deadlink = bpy.props.IntProperty() + bpy.types.Scene.mol_totallink = bpy.props.IntProperty() + bpy.types.Scene.mol_totaldeadlink = bpy.props.IntProperty() + bpy.types.Scene.mol_objuvbake = bpy.props.StringProperty() + bpy.types.Scene.mol_psysuvbake = bpy.props.StringProperty() + bpy.types.Scene.mol_stime = bpy.props.FloatProperty() diff --git a/molecular/simulate.py b/molecular/simulate.py new file mode 100644 index 0000000..e8d7d92 --- /dev/null +++ b/molecular/simulate.py @@ -0,0 +1,146 @@ +import math + +import bpy + +from .utils import get_object + + +def pack_data(context, initiate): + psyslen = 0 + parnum = 0 + scene = context.scene + for ob in bpy.data.objects: + obj = get_object(context, ob) + + for psys in obj.particle_systems: + if psys.settings.mol_matter != "-1": + psys.settings.mol_density = float(psys.settings.mol_matter) + if psys.settings.mol_active and len(psys.particles): + parlen = len(psys.particles) + par_loc = [0, 0, 0] * parlen + par_vel = [0, 0, 0] * parlen + par_size = [0] * parlen + par_alive = [] + for par in psys.particles: + parnum += 1 + if par.alive_state == "UNBORN": + par_alive.append(2) + if par.alive_state == "ALIVE": + par_alive.append(0) + if par.alive_state == "DEAD": + par_alive.append(3) + + psys.particles.foreach_get('location', par_loc) + psys.particles.foreach_get('velocity', par_vel) + + if initiate: + par_mass = [] + + if psys.settings.mol_density_active: + for par in psys.particles: + par_mass.append(psys.settings.mol_density * (4 / 3 * math.pi * ((par.size / 2) ** 3))) + else: + for par in psys.particles: + par_mass.append(psys.settings.mass) + + """ + if scene.mol_timescale_active == True: + psys.settings.timestep = 1 / (scene.render.fps / scene.timescale) + else: + psys.settings.timestep = 1 / scene.render.fps + """ + + psyslen += 1 + psys.particles.foreach_get('size', par_size) + if bpy.context.scene.mol_minsize > min(par_size): + bpy.context.scene.mol_minsize = min(par_size) + + if psys.settings.mol_link_samevalue: + psys.settings.mol_link_estiff = psys.settings.mol_link_stiff + psys.settings.mol_link_estiffrand = psys.settings.mol_link_stiffrand + psys.settings.mol_link_estiffexp = psys.settings.mol_link_stiffexp + psys.settings.mol_link_edamp = psys.settings.mol_link_damp + psys.settings.mol_link_edamprand = psys.settings.mol_link_damprand + psys.settings.mol_link_ebroken = psys.settings.mol_link_broken + psys.settings.mol_link_ebrokenrand = psys.settings.mol_link_brokenrand + + if psys.settings.mol_relink_samevalue: + psys.settings.mol_relink_estiff = psys.settings.mol_relink_stiff + psys.settings.mol_relink_estiffrand = psys.settings.mol_relink_stiffrand + psys.settings.mol_relink_estiffexp = psys.settings.mol_relink_stiffexp + psys.settings.mol_relink_edamp = psys.settings.mol_relink_damp + psys.settings.mol_relink_edamprand = psys.settings.mol_relink_damprand + psys.settings.mol_relink_ebroken = psys.settings.mol_relink_broken + psys.settings.mol_relink_ebrokenrand = psys.settings.mol_relink_brokenrand + + params = [0] * 47 + + params[0] = psys.settings.mol_selfcollision_active + params[1] = psys.settings.mol_othercollision_active + params[2] = psys.settings.mol_collision_group + params[3] = psys.settings.mol_friction + params[4] = psys.settings.mol_collision_damp + params[5] = psys.settings.mol_links_active + + if psys.settings.mol_link_rellength: + params[6] = psys.settings.particle_size * psys.settings.mol_link_length + else: + params[6] = psys.settings.mol_link_length + + params[7] = psys.settings.mol_link_max + params[8] = psys.settings.mol_link_tension + params[9] = psys.settings.mol_link_tensionrand + params[10] = psys.settings.mol_link_stiff + params[11] = psys.settings.mol_link_stiffrand + params[12] = psys.settings.mol_link_stiffexp + params[13] = psys.settings.mol_link_damp + params[14] = psys.settings.mol_link_damprand + params[15] = psys.settings.mol_link_broken + params[16] = psys.settings.mol_link_brokenrand + params[17] = psys.settings.mol_link_estiff + params[18] = psys.settings.mol_link_estiffrand + params[19] = psys.settings.mol_link_estiffexp + params[20] = psys.settings.mol_link_edamp + params[21] = psys.settings.mol_link_edamprand + params[22] = psys.settings.mol_link_ebroken + params[23] = psys.settings.mol_link_ebrokenrand + params[24] = psys.settings.mol_relink_group + params[25] = psys.settings.mol_relink_chance + params[26] = psys.settings.mol_relink_chancerand + params[27] = psys.settings.mol_relink_max + params[28] = psys.settings.mol_relink_tension + params[29] = psys.settings.mol_relink_tensionrand + params[30] = psys.settings.mol_relink_stiff + params[31] = psys.settings.mol_relink_stiffexp + params[32] = psys.settings.mol_relink_stiffrand + params[33] = psys.settings.mol_relink_damp + params[34] = psys.settings.mol_relink_damprand + params[35] = psys.settings.mol_relink_broken + params[36] = psys.settings.mol_relink_brokenrand + params[37] = psys.settings.mol_relink_estiff + params[38] = psys.settings.mol_relink_estiffexp + params[39] = psys.settings.mol_relink_estiffrand + params[40] = psys.settings.mol_relink_edamp + params[41] = psys.settings.mol_relink_edamprand + params[42] = psys.settings.mol_relink_ebroken + params[43] = psys.settings.mol_relink_ebrokenrand + params[44] = psys.settings.mol_link_friction + params[45] = psys.settings.mol_link_group + params[46] = psys.settings.mol_other_link_active + + mol_exportdata = bpy.context.scene.mol_exportdata + + if initiate: + mol_exportdata[0][2] = psyslen + mol_exportdata[0][3] = parnum + mol_exportdata.append(( + parlen, + par_loc, + par_vel, + par_size, + par_mass, + par_alive, + params + )) + else: + mol_exportdata.append((par_loc, par_vel, par_alive)) diff --git a/molecular/sources/setup.bat b/molecular/sources/setup.bat deleted file mode 100644 index 9444896..0000000 --- a/molecular/sources/setup.bat +++ /dev/null @@ -1 +0,0 @@ -python setup.py build_ext --inplace \ No newline at end of file diff --git a/molecular/sources/setup.py b/molecular/sources/setup.py deleted file mode 100644 index 3134d22..0000000 --- a/molecular/sources/setup.py +++ /dev/null @@ -1,15 +0,0 @@ -from distutils.core import setup -from distutils.extension import Extension -from Cython.Distutils import build_ext -import Cython.Compiler.Options - -Cython.Compiler.Options.annotate = True - -#ext_modules = [Extension("cmolcore", ["cmolcore.pyx"],extra_compile_args=['-march=i686','-fopenmp'],extra_link_args=['-fopenmp'])] -ext_modules = [Extension("cmolcore", ["cmolcore.pyx"],extra_compile_args=['-march=i686','-O3','-ffast-math','-fopenmp'],extra_link_args=['-fopenmp'])] - -setup( - name = 'Molecular script', - cmdclass = {'build_ext': build_ext}, - ext_modules = ext_modules -) diff --git a/molecular/sources/setup64.bat b/molecular/sources/setup64.bat deleted file mode 100644 index 0cb9fce..0000000 --- a/molecular/sources/setup64.bat +++ /dev/null @@ -1,3 +0,0 @@ -D:\Python33_x64\python.exe" setup64.py build_ext --inplace - -"C:\Program Files\Microsoft SDKs\Windows\v7.0\Bin\mt.exe" -inputresource:D:\stuff\blender-2.66a-windows64\python33.dll;#2 -outputresource:D:\sandbox\blender_db\new_substep_molecular\cmolcore.pyd;#2 diff --git a/molecular/sources/setup64.py b/molecular/sources/setup64.py deleted file mode 100644 index 39306e1..0000000 --- a/molecular/sources/setup64.py +++ /dev/null @@ -1,14 +0,0 @@ -from distutils.core import setup -from distutils.extension import Extension -from Cython.Distutils import build_ext -import Cython.Compiler.Options - -Cython.Compiler.Options.annotate = True - -ext_modules = [Extension("cmolcore", ["cmolcore.pyx"],extra_compile_args=['/Ox','/openmp','/GT','/arch:SSE2','/fp:fast'])] - -setup( - name = 'Molecular script', - cmdclass = {'build_ext': build_ext}, - ext_modules = ext_modules -) diff --git a/molecular/ui.py b/molecular/ui.py new file mode 100644 index 0000000..eee6bda --- /dev/null +++ b/molecular/ui.py @@ -0,0 +1,470 @@ +import bpy + +from . import names +from .utils import get_object + + +class MolecularBasePanel(bpy.types.Panel): + bl_space_type = 'PROPERTIES' + bl_region_type = 'WINDOW' + bl_context = "particle" + bl_options = {'DEFAULT_CLOSED', } + + @classmethod + def poll(cls, context): + return context.object.particle_systems.active + + +class MolecularDensityPanel(MolecularBasePanel): + bl_label = names.DENSITY + bl_idname = "OBJECT_PT_molecular_density" + bl_parent_id = 'OBJECT_PT_molecular' + + def draw(self, context): + layout = self.layout + scn = bpy.context.scene + obj = context.object + psys = obj.particle_systems.active + if psys is None: + return + layout.enabled = psys.settings.mol_active + # for the data + psys_eval = get_object(context, context.object).particle_systems.active + + layout.prop(psys.settings, "mol_density_active", toggle=True) + + if not psys.settings.mol_density_active: + return + + layout.prop(psys.settings, "mol_matter") + + if int(psys.settings.mol_matter) >= 1: + weight_text = "Total system approx weight: {0:.4} kg".format( + psys.settings.mol_matter + ) + layout.label(icon="INFO", text=weight_text) + return + + layout.prop(psys.settings, "mol_density") + pmass = (psys.settings.particle_size ** 3) * psys.settings.mol_density + weight_text = "Total system approx weight: {0:.2f} kg".format( + len(psys_eval.particles) * pmass + ) + layout.label(icon="INFO", text=weight_text) + + +class MolecularCollisionPanel(MolecularBasePanel): + bl_label = names.COLLISION + bl_idname = "OBJECT_PT_molecular_collision" + bl_parent_id = 'OBJECT_PT_molecular' + + def draw(self, context): + layout = self.layout + scn = bpy.context.scene + obj = context.object + psys = obj.particle_systems.active + if psys is None: + return + # particle system settings + stg = psys.settings + + layout.enabled = stg.mol_active + # for the data + psys_eval = get_object(context, context.object).particle_systems.active + + layout.prop(stg, "mol_selfcollision_active", toggle=True) + layout.prop(stg, "mol_othercollision_active", toggle=True) + if stg.mol_othercollision_active: + layout.prop(stg, "mol_collision_group") + if stg.mol_selfcollision_active or stg.mol_othercollision_active: + layout.prop(stg, "mol_friction") + layout.prop(stg, "mol_collision_damp") + + +class MolecularLinksPanel(MolecularBasePanel): + bl_label = names.LINKS + bl_idname = "OBJECT_PT_molecular_links" + bl_parent_id = 'OBJECT_PT_molecular' + + def draw(self, context): + layout = self.layout + scn = bpy.context.scene + obj = context.object + psys = obj.particle_systems.active + if psys is None: + return + layout.enabled = psys.settings.mol_active + + layout.prop(psys.settings, "mol_links_active", toggle=True) + layout.prop(psys.settings, "mol_other_link_active", toggle=True) + if psys.settings.mol_other_link_active: + layout.prop(psys.settings, "mol_link_group") + + +class MolecularInitLinksPanel(MolecularBasePanel): + bl_label = names.INITIAL_LINKING + bl_idname = "OBJECT_PT_molecular_init_links" + bl_parent_id = 'OBJECT_PT_molecular_links' + + def draw(self, context): + layout = self.layout + scn = bpy.context.scene + obj = context.object + psys = obj.particle_systems.active + if psys is None: + return + stg = psys.settings + layout.enabled = stg.mol_active and ( + stg.mol_links_active or stg.mol_other_link_active + ) + + row = layout.row() + row.prop(psys.settings, "mol_link_length") + row.prop(psys.settings, "mol_link_rellength") + row = layout.row() + row.prop(psys.settings, "mol_link_max") + row = layout.row() + row.prop(psys.settings, "mol_link_friction") + layout.separator() + row = layout.row() + row.prop(psys.settings, "mol_link_tension") + row.prop(psys.settings, "mol_link_tensionrand") + row = layout.row() + row.prop(psys.settings, "mol_link_samevalue", toggle=True) + if not psys.settings.mol_link_samevalue: + layout.label(text='Compression:') + row = layout.row() + row.prop(psys.settings, "mol_link_stiff") + row.prop(psys.settings, "mol_link_stiffrand") + #row = layout.row() + #row.prop(psys.settings, "mol_link_stiffexp") + row = layout.row() + row.prop(psys.settings, "mol_link_damp") + row.prop(psys.settings, "mol_link_damprand") + row = layout.row() + row.prop(psys.settings, "mol_link_broken") + row.prop(psys.settings, "mol_link_brokenrand") + if not psys.settings.mol_link_samevalue: + layout.label(text='Expansion:') + row = layout.row() + row.enabled = not psys.settings.mol_link_samevalue + row.prop(psys.settings, "mol_link_estiff") + row.prop(psys.settings, "mol_link_estiffrand") + #row = layout.row() + #row.enabled = not psys.settings.mol_link_samevalue + #row.prop(psys.settings, "mol_link_estiffexp") + row = layout.row() + row.enabled = not psys.settings.mol_link_samevalue + row.prop(psys.settings, "mol_link_edamp") + row.prop(psys.settings, "mol_link_edamprand") + row = layout.row() + row.enabled = not psys.settings.mol_link_samevalue + row.prop(psys.settings, "mol_link_ebroken") + row.prop(psys.settings, "mol_link_ebrokenrand") + + +class MolecularNewLinksPanel(MolecularBasePanel): + bl_label = names.NEW_LINKING + bl_idname = "OBJECT_PT_molecular_new_links" + bl_parent_id = 'OBJECT_PT_molecular_links' + + def draw(self, context): + layout = self.layout + scn = bpy.context.scene + obj = context.object + psys = obj.particle_systems.active + if psys is None: + return + stg = psys.settings + + layout.enabled = stg.mol_active and ( + stg.mol_links_active or stg.mol_other_link_active + ) + row = layout.row() + row.prop(psys.settings, "mol_relink_group") + row = layout.row() + row.prop(psys.settings, "mol_relink_chance") + row.prop(psys.settings, "mol_relink_chancerand") + row = layout.row() + row.prop(psys.settings, "mol_relink_max") + row = layout.row() + layout.separator() + row = layout.row() + row.prop(psys.settings,"mol_relink_tension") + row.prop(psys.settings,"mol_relink_tensionrand") + row = layout.row() + row.prop(psys.settings, "mol_relink_samevalue", toggle=True) + if not psys.settings.mol_relink_samevalue: + layout.label(text='Compression:') + row = layout.row() + row.prop(psys.settings,"mol_relink_stiff") + row.prop(psys.settings,"mol_relink_stiffrand") + #row = layout.row() + #row.prop(psys.settings, "mol_relink_stiffexp") + row = layout.row() + row.prop(psys.settings, "mol_relink_damp") + row.prop(psys.settings, "mol_relink_damprand") + row = layout.row() + row.prop(psys.settings, "mol_relink_broken") + row.prop(psys.settings, "mol_relink_brokenrand") + row = layout.row() + if not psys.settings.mol_relink_samevalue: + layout.label(text='Expansion:') + row = layout.row() + row.prop(psys.settings, "mol_relink_estiff") + row.prop(psys.settings, "mol_relink_estiffrand") + #row = layout.row() + #row.enabled = not psys.settings.mol_relink_samevalue + #row.prop(psys.settings, "mol_relink_estiffexp") + row = layout.row() + row.prop(psys.settings, "mol_relink_edamp") + row.prop(psys.settings, "mol_relink_edamprand") + row = layout.row() + row.prop(psys.settings, "mol_relink_ebroken") + row.prop(psys.settings, "mol_relink_ebrokenrand") + + +class MolecularSimulatePanel(MolecularBasePanel): + bl_label = names.SIMULATE + bl_idname = "OBJECT_PT_molecular_simulate" + bl_parent_id = 'OBJECT_PT_molecular' + + def draw(self, context): + layout = self.layout + scn = bpy.context.scene + obj = context.object + psys = obj.particle_systems.active + if psys is None: + return + layout.enabled = psys.settings.mol_active + # for the data + psys_eval = get_object(context, context.object).particle_systems.active + + layout.prop(scn, "frame_start", text="Start Frame") + layout.prop(scn, "frame_end", text="End Frame") + + # row = layout.row() + # row.prop(scn,"mol_timescale_active", text="Activate TimeScaling") + # row = layout.row() + # row.enabled = scn.mol_timescale_active + # row.prop(scn, "timescale", text="Time Scale") + + layout.prop(scn, "mol_substep") + layout.prop(scn, "mol_cpu", text=names.CPU_USED) + layout.prop(scn, "mol_bake") + layout.prop(scn, "mol_render") + + row = layout.row() + + if obj.data.uv_layers.active != None: + row.prop( + psys.settings, + "mol_bakeuv", + text="Bake UV at ending (current: \"{0}\")".format( + obj.data.uv_layers.active.name + ) + ) + else: + row.active = False + row.prop( + psys.settings, + "mol_bakeuv", + text="Bake UV at ending (current: None)" + ) + + row = layout.row() + icon = 'PARTICLE_DATA' + + if scn.mol_simrun == False and psys.point_cache.is_baked == False: + row.enabled = True + row.operator( + "object.mol_simulate", + icon=icon, + text="Start Molecular Simulation" + ) + row = layout.row() + row.enabled = False + row.operator("ptcache.free_bake_all", text="Free All Bakes") + + if psys.point_cache.is_baked == True and scn.mol_simrun == False: + row.enabled = False + row.operator( + "object.mol_simulate", + icon=icon, + text="Simulation baked" + ) + row = layout.row() + row.enabled = True + row.operator("ptcache.free_bake_all", text="Free All Bakes") + + if scn.mol_simrun == True: + row.enabled = False + row.operator( + "object.mol_simulate", + icon=icon, + text="Process: {} left".format(scn.mol_timeremain) + ) + row = layout.row() + row.enabled = False + row.operator("ptcache.free_bake_all", text="Free All Bakes") + + +class MolecularToolsPanel(MolecularBasePanel): + bl_label = names.MOLECULAR_TOOLS + bl_idname = "OBJECT_PT_molecular_tools" + bl_parent_id = 'OBJECT_PT_molecular' + + def draw(self, context): + layout = self.layout + scn = bpy.context.scene + obj = context.object + psys = obj.particle_systems.active + if psys is None: + return + layout.enabled = psys.settings.mol_active + # for the data + psys_eval = get_object(context, context.object).particle_systems.active + + box = layout.box() + row = box.row() + row.label(text=names.PARTICLE_UV) + row = box.row() + row.alignment = 'CENTER' + row.label(icon='INFO', text=names.SET_POSITION) + row = box.row() + row.alignment = 'CENTER' + row.label(text=names.UV_HELP) + row = box.row() + row.alignment = 'CENTER' + row.label(text=names.CYCLES_HELP) + row = box.row() + row.operator( + "object.mol_set_global_uv", + icon='GROUP_UVS', + text="Set Global UV" + ) + row = box.row() + + if obj.data.uv_layers.active != None: + row.operator( + "object.mol_set_active_uv", + icon='GROUP_UVS', + text = "Set Active UV (current: \"{0}\")".format( + obj.data.uv_layers.active.name + ) + ) + else: + row.active = False + row.operator( + "object.mol_set_active_uv", + icon='GROUP_UVS', + text="Set Active UV (no uvs found)" + ) + + box = layout.box() + row = box.row() + row.label(text=names.SUBSTEPS_CALCULATOR) + row = box.row() + row.label( + icon='INFO', + text="Current systems have: {} particles".format(len(psys_eval.particles)) + ) + row = box.row() + row.prop(psys.settings, "mol_var1") + row = box.row() + row.prop(psys.settings,"mol_var2") + row = box.row() + row.prop(psys.settings,"mol_var3") + diff = (psys.settings.mol_var3 / psys.settings.mol_var1) + factor = psys.settings.mol_var3 ** (1 / 3) / psys.settings.mol_var1 ** (1 / 3) + newsubstep = int(round(factor * psys.settings.mol_var2)) + row = box.row() + row.label( + icon='FORWARD', + text="You must set new substep to: {}".format(newsubstep) + ) + row = box.row() + row.label( + icon='ERROR', + text="Multiply particles size by: {}".format(round(1 / factor, 5)) + ) + row = box.row() + row.label( + icon='ERROR', + text="Multiply others sys particle number by: {}".format(round(diff, 5)) + ) + + +class MolecularAboutPanel(MolecularBasePanel): + bl_label = 'About' + bl_idname = "OBJECT_PT_molecular_about" + bl_parent_id = 'OBJECT_PT_molecular' + + def draw(self, context): + layout = self.layout + scn = bpy.context.scene + obj = context.object + psys = obj.particle_systems.active + if psys is None: + return + layout.enabled = psys.settings.mol_active + # for the data + psys_eval = get_object(context, context.object).particle_systems.active + + box = layout.box() + row = box.row() + box.active = False + box.alert = False + row.alignment = 'CENTER' + row.label(text=names.THANKS) + row = box.row() + row.alignment = 'CENTER' + row.label(text=names.SUPPORT_WORK) + row = box.row() + row.alignment = 'CENTER' + row.operator( + "wm.url_open", + text="click here to Donate", + icon='URL' + ).url = "www.pyroevil.com/donate/" + row = box.row() + row.alignment = 'CENTER' + row.label(text=names.VISIT) + row = box.row() + row.alignment = 'CENTER' + row.label(text=names.SITE) + + +class MolecularPanel(MolecularBasePanel): + """Creates a Panel in the Object properties window""" + bl_label = "Molecular" + bl_idname = "OBJECT_PT_molecular" + + def draw_header(self, context): + layout = self.layout + obj = context.object + psys = obj.particle_systems.active + row = layout.row() + if not psys is None: + row.prop(psys.settings, "mol_active", text='') + + def draw(self, context): + layout = self.layout + obj = context.object + psys = obj.particle_systems.active + if psys is None: + return + layout.enabled = psys.settings.mol_active + + +panel_classes = ( + MolecularPanel, + MolecularSimulatePanel, + MolecularDensityPanel, + MolecularCollisionPanel, + MolecularLinksPanel, + MolecularInitLinksPanel, + MolecularNewLinksPanel, + MolecularToolsPanel, + MolecularAboutPanel +) diff --git a/molecular/utils.py b/molecular/utils.py new file mode 100644 index 0000000..a092b47 --- /dev/null +++ b/molecular/utils.py @@ -0,0 +1,15 @@ +import bpy + + +def get_object(context, obj): + depsgraph = context.evaluated_depsgraph_get() + return obj.evaluated_get(depsgraph) + + +def destroy_caches(obj): + for psys in obj.particle_systems: + # attempt to destroy cache prior to resimulation + # by provoking an internal RNA update call, this will also update the psys for get_object + if psys.settings.mol_active: + step = psys.point_cache.frame_step + psys.point_cache.frame_step = step diff --git a/release/molecular_20130106.zip b/release/molecular_20130106.zip deleted file mode 100644 index 4f44db3..0000000 Binary files a/release/molecular_20130106.zip and /dev/null differ diff --git a/release/molecular_20130203.zip b/release/molecular_20130203.zip deleted file mode 100644 index 6778cad..0000000 Binary files a/release/molecular_20130203.zip and /dev/null differ diff --git a/release/molecular_latest.zip b/release/molecular_latest.zip deleted file mode 100644 index 6778cad..0000000 Binary files a/release/molecular_latest.zip and /dev/null differ diff --git a/molecular/sources/cmolcore.pyx b/sources/core.pyx similarity index 61% rename from molecular/sources/cmolcore.pyx rename to sources/core.pyx index 0549217..d2157e8 100644 --- a/molecular/sources/cmolcore.pyx +++ b/sources/core.pyx @@ -2,22 +2,31 @@ #cython: boundscheck=False #cython: cdivision=True -# NOTE: order of slow fonction to be optimize/multithreaded: kdtreesearching , kdtreecreating , linksolving +# NOTE: order of slow fonction to be optimize/multithreaded: +# kdtreesearching, kdtreecreating, linksolving + cimport cython -from time import clock -from cython.parallel import parallel , prange , threadid -from libc.stdlib cimport malloc , realloc, free , rand , srand, abs +from time import perf_counter as clock +from cython.parallel import parallel, prange, threadid +from libc.stdlib cimport malloc, realloc, free, rand, srand, abs + cdef extern from *: int INT_MAX float FLT_MAX + cdef extern from "stdlib.h": ctypedef void const_void "const void" - void qsort(void *base, int nmemb, int size,int(*compar)(const_void *, const_void *)) nogil + void qsort( + void *base, + int nmemb, + int size, + int(*compar)(const_void *, const_void *) + )nogil + - cdef float fps = 0 cdef int substep = 0 cdef float deltatime = 0 @@ -32,7 +41,8 @@ cdef Particle *parlist = NULL cdef SParticle *parlistcopy = NULL cdef ParSys *psys = NULL cdef KDTree *kdtree = NULL -print("cmolcore imported with success! v1.01") +print("cmolcore imported with success! v1.12") + cpdef init(importdata): global fps @@ -57,23 +67,21 @@ cpdef init(importdata): totaldeadlinks = 0 fps = float(importdata[0][0]) substep = int(importdata[0][1]) - deltatime = (fps * (substep +1)) + deltatime = (fps * (substep + 1)) psysnum = importdata[0][2] parnum = importdata[0][3] cpunum = importdata[0][4] - deadlinks = malloc( cpunum * cython.sizeof(int) ) - print " Number of cpu's used:",cpunum - psys = malloc( psysnum * cython.sizeof(ParSys) ) - #printdb(40) - parlist = malloc( parnum * cython.sizeof(Particle) ) - #printdb(parnum) - parlistcopy = malloc( parnum * cython.sizeof(SParticle) ) + deadlinks = malloc(cpunum * cython.sizeof(int)) + print(" Number of cpu's used:", cpunum) + psys = malloc(psysnum * cython.sizeof(ParSys)) + parlist = malloc(parnum * cython.sizeof(Particle)) + parlistcopy = malloc(parnum * cython.sizeof(SParticle)) cdef int jj = 0 - #printdb(47) for i in xrange(psysnum): psys[i].id = i - psys[i].parnum = importdata[i+1][0] - psys[i].particles = malloc( psys[i].parnum * cython.sizeof(Particle) ) + psys[i].parnum = importdata[i + 1][0] + psys[i].particles = malloc(psys[i].parnum * \ + cython.sizeof(Particle)) psys[i].particles = &parlist[jj] for ii in xrange(psys[i].parnum): parlist[jj].id = jj @@ -88,7 +96,7 @@ cpdef init(importdata): parlist[jj].state = importdata[i + 1][5][ii] psys[i].selfcollision_active = importdata[i + 1][6][0] psys[i].othercollision_active = importdata[i + 1][6][1] - psys[i].collision_group = int(importdata[i + 1][6][2]) + psys[i].collision_group = importdata[i + 1][6][2] psys[i].friction = importdata[i + 1][6][3] psys[i].collision_damp = importdata[i + 1][6][4] psys[i].links_active = importdata[i + 1][6][5] @@ -110,7 +118,7 @@ cpdef init(importdata): psys[i].link_edamprand = importdata[i + 1][6][21] psys[i].link_ebroken = importdata[i + 1][6][22] psys[i].link_ebrokenrand = importdata[i + 1][6][23] - psys[i].relink_group = int(importdata[i + 1][6][24]) + psys[i].relink_group = importdata[i + 1][6][24] psys[i].relink_chance = importdata[i + 1][6][25] psys[i].relink_chancerand = importdata[i + 1][6][26] psys[i].relink_max = importdata[i + 1][6][27] @@ -131,91 +139,121 @@ cpdef init(importdata): psys[i].relink_ebroken = importdata[i + 1][6][42] psys[i].relink_ebrokenrand = importdata[i + 1][6][43] psys[i].link_friction = importdata[i + 1][6][44] - + psys[i].link_group = importdata[i + 1][6][45] + psys[i].other_link_active = importdata[i + 1][6][46] + parlist[jj].sys = &psys[i] - parlist[jj].collided_with = malloc( 1 * cython.sizeof(int) ) + parlist[jj].collided_with = malloc(1 * cython.sizeof(int)) parlist[jj].collided_num = 0 - parlist[jj].links = malloc( 1 * cython.sizeof(Links) ) + parlist[jj].links = malloc(1 * cython.sizeof(Links)) parlist[jj].links_num = 0 parlist[jj].links_activnum = 0 - parlist[jj].link_with = malloc( 1 * cython.sizeof(int) ) + parlist[jj].link_with = malloc(1 * cython.sizeof(int)) parlist[jj].link_withnum = 0 parlist[jj].neighboursmax = 10 - parlist[jj].neighbours = malloc( parlist[jj].neighboursmax * cython.sizeof(int) ) + parlist[jj].neighbours = malloc(parlist[jj].neighboursmax * \ + cython.sizeof(int)) parlist[jj].neighboursnum = 0 jj += 1 - + jj = 0 - #printdb(115) - kdtree = malloc( 1 * cython.sizeof(KDTree) ) - #printdb(115) - KDTree_create_nodes(kdtree,parnum) - #printdb(117) + kdtree = malloc(1 * cython.sizeof(KDTree)) + KDTree_create_nodes(kdtree, parnum) + with nogil: - for i in prange(parnum,schedule='dynamic',chunksize=10,num_threads=cpunum): + for i in prange( + parnum, + schedule='dynamic', + chunksize=10, + num_threads=cpunum + ): parlistcopy[i].id = parlist[i].id parlistcopy[i].loc[0] = parlist[i].loc[0] parlistcopy[i].loc[1] = parlist[i].loc[1] parlistcopy[i].loc[2] = parlist[i].loc[2] - - #printdb(119) - KDTree_create_tree(kdtree,parlistcopy,0,parnum - 1,0,-1,0,1) - #printdb(120) + + KDTree_create_tree(kdtree, parlistcopy, 0, parnum - 1, 0, -1, 0, 1) + with nogil: - for i in prange(kdtree.thread_index,schedule='dynamic',chunksize=10,num_threads=cpunum): - KDTree_create_tree(kdtree,parlistcopy,kdtree.thread_start[i],kdtree.thread_end[i],kdtree.thread_name[i],kdtree.thread_parent[i],kdtree.thread_depth[i],0) - #printdb(121) + for i in prange( + kdtree.thread_index, + schedule='dynamic', + chunksize=10, + num_threads=cpunum + ): + KDTree_create_tree( + kdtree, + parlistcopy, + kdtree.thread_start[i], + kdtree.thread_end[i], + kdtree.thread_name[i], + kdtree.thread_parent[i], + kdtree.thread_depth[i], + 0 + ) + with nogil: - for i in prange(parnum,schedule='dynamic',chunksize=10,num_threads=cpunum): + for i in prange( + parnum, + schedule='dynamic', + chunksize=10, + num_threads=cpunum + ): if parlist[i].sys.links_active == 1: - KDTree_rnn_query(kdtree,&parlist[i],parlist[i].loc,parlist[i].sys.link_length) - #printdb(122) + KDTree_rnn_query( + kdtree, + &parlist[i], + parlist[i].loc, + parlist[i].sys.link_length + ) + for i in xrange(parnum): - #printdb(123) - create_link(parlist[i].id,parlist[i].sys.link_max) + create_link(parlist[i].id, parlist[i].sys.link_max) if parlist[i].neighboursnum > 1: - #printdb(124) - #free(parlist[i].neighbours) + # free(parlist[i].neighbours) parlist[i].neighboursnum = 0 - #printdb(125) - #printdb(126) - #testkdtree(3) totallinks += newlinks - print " New links created: ",newlinks + print(" New links created: ", newlinks) return parnum - - -cdef void printdb (int linenumber, text = ""): + + +cdef void printdb(int linenumber, text = ""): cdef int dbactive = 1 if dbactive == 1: print(linenumber) + cdef testkdtree(int verbose = 0): global kdtree global parnum if verbose >= 3: - print("RootNode:",kdtree.root_node[0].index) + print("RootNode:", kdtree.root_node[0].index) for i in xrange(parnum): - print("Parent",kdtree.nodes[i].index,"Particle:",kdtree.nodes[i].particle[0].id) - print(" Left",kdtree.nodes[i].left_child[0].index) - print(" Right",kdtree.nodes[i].right_child[0].index) + print( + "Parent", + kdtree.nodes[i].index, + "Particle:", + kdtree.nodes[i].particle[0].id + ) + print(" Left", kdtree.nodes[i].left_child[0].index) + print(" Right", kdtree.nodes[i].right_child[0].index) - cdef float *a = [0,0,0] + cdef float *a = [0, 0, 0] cdef Particle *b - b = malloc( 1 * cython.sizeof(Particle) ) + b = malloc(1 * cython.sizeof(Particle)) if verbose >= 1: print("start searching") - KDTree_rnn_query(kdtree,b,a,2) + KDTree_rnn_query(kdtree, b, a, 2) output = [] if verbose >= 2: print("Result") for i in xrange(b[0].neighboursnum): - print(" Query Particle:",parlist[b[0].neighbours[i]].id) + print(" Query Particle:", parlist[b[0].neighbours[i]].id) if verbose >= 1: - print("number of particle find:",b[0].neighboursnum) + print("number of particle find:", b[0].neighboursnum) free(b) - - + + cpdef simulate(importdata): global kdtree global parlist @@ -229,8 +267,8 @@ cpdef simulate(importdata): global totallinks global totaldeadlinks global deadlinks - - cdef int i= 0 + + cdef int i = 0 cdef int ii = 0 cdef int profiling = 0 @@ -241,33 +279,32 @@ cpdef simulate(importdata): cdef float maxY = -INT_MAX cdef float maxZ = -INT_MAX cdef float maxSize = -INT_MAX - cdef Pool *parPool = malloc( 1 * cython.sizeof(Pool) ) - parPool.parity = malloc( 2 * cython.sizeof(Parity) ) + cdef Pool *parPool = malloc(1 * cython.sizeof(Pool)) + parPool.parity = malloc(2 * cython.sizeof(Parity)) parPool[0].axis = -1 parPool[0].offset = 0 parPool[0].max = 0 - #cdef float *zeropoint = [0,0,0] + # cdef float *zeropoint = [0,0,0] newlinks = 0 for i in xrange(cpunum): deadlinks[i] = 0 - #printdb(140) if profiling == 1: print("-->start simulate") stime2 = clock() stime = clock() - + update(importdata) - + if profiling == 1: - print("-->update time", clock() - stime,"sec") + print("-->update time", clock() - stime, "sec") stime = clock() - #printdb(145) + for i in xrange(parnum): parlistcopy[i].id = parlist[i].id parlistcopy[i].loc[0] = parlist[i].loc[0] - #if parlist[i].loc[0] >= FLT_MAX or parlist[i].loc[0] <= -FLT_MAX : - #print('ALERT! INF value in X') + # if parlist[i].loc[0] >= FLT_MAX or parlist[i].loc[0] <= -FLT_MAX : + # print('ALERT! INF value in X') if parlist[i].loc[0] < minX: minX = parlist[i].loc[0] if parlist[i].loc[0] > maxX: @@ -289,27 +326,25 @@ cpdef simulate(importdata): maxSize = parlist[i].links[ii].lenght if (parlist[i].size * 2) > maxSize: maxSize = (parlist[i].size * 2) - #''' + if (maxX - minX) >= (maxY - minY) and (maxX - minX) >= (maxZ - minZ): parPool[0].axis = 0 parPool[0].offset = 0 - minX parPool[0].max = maxX + parPool[0].offset - + if (maxY - minY) > (maxX - minX) and (maxY - minY) > (maxZ - minZ): parPool[0].axis = 1 parPool[0].offset = 0 - minY parPool[0].max = maxY + parPool[0].offset - + if (maxZ - minZ) > (maxY - minY) and (maxZ - minZ) > (maxX - minX): parPool[0].axis = 2 parPool[0].offset = 0 - minZ parPool[0].max = maxZ + parPool[0].offset - + if (parPool[0].max / ( cpunum * 10 )) > maxSize: maxSize = (parPool[0].max / ( cpunum * 10 )) - - #printdb(155) - + ''' cdef float Xsize = maxX - minX cdef float Ysize = maxY - minY @@ -317,7 +352,6 @@ cpdef simulate(importdata): cdef float newXsize = Xsize cdef float newYsize = Ysize cdef float newZsize = Zsize - #print(Xsize,Ysize,Zsize) pyaxis = [] for i in xrange(64): if Xsize >= Ysize and Xsize >= Zsize: @@ -334,117 +368,145 @@ cpdef simulate(importdata): Ysize = newYsize Zsize = newZsize pyaxis.append(kdtree.axis[i]) - #print(pyaxis) - ''' - - + ''' + cdef int pair cdef int heaps cdef float scale = 1 / ( maxSize * 2.1 ) - #printdb(160) - #print('minX:',minX,'maxX:',maxX) - #print('minY:',minY,'maxY:',maxY) - #print('minZ:',minZ,'maxZ:',maxZ) - #print('Maxsize:',maxSize) - #print('Max divide by square of CPU numbers:',(parPool[0].max / ( cpunum * 8 ))) - #print('Axe:',parPool[0].axis) - #print('Offset:',parPool[0].offset) - #print('Max:',parPool[0].max) - #print('Scale:',scale) + for pair in xrange(2): - #print("i:",i) - parPool[0].parity[pair].heap = malloc(((parPool[0].max * scale) + 1) * cython.sizeof(Heap) ) + + parPool[0].parity[pair].heap = \ + malloc(((parPool[0].max * scale) + 1) * \ + cython.sizeof(Heap)) + for heaps in range((parPool[0].max * scale) + 1): - #print("1- pair:",pair,"heaps:",heaps,"particles:",0) parPool[0].parity[pair].heap[heaps].parnum = 0 parPool[0].parity[pair].heap[heaps].maxalloc = 50 - parPool[0].parity[pair].heap[heaps].par = malloc( parPool[0].parity[pair].heap[heaps].maxalloc * cython.sizeof(int) ) - #printdb(165) + + parPool[0].parity[pair].heap[heaps].par = \ + malloc(parPool[0].parity[pair].heap[heaps].maxalloc * \ + cython.sizeof(int)) + for i in xrange(parnum): - #printdb(166) - #print('axis:',parPool[0].axis) - #print('position:',parlist[i].loc[parPool[0].axis]) - #print('offset:',parPool[0].offset) - #print('scale:',scale) - #print('non-int pair',(((parlist[i].loc[parPool[0].axis] + parPool[0].offset) * scale) % 2)) - pair = (((parlist[i].loc[parPool[0].axis] + parPool[0].offset) * scale) % 2) - heaps = ((parlist[i].loc[parPool[0].axis] + parPool[0].offset) * scale) - #print("- pair:",pair,"heaps:",heaps,"particles:",parlist[i].id) - #print(parPool[0].parity[pair].heap[heaps].parnum) + pair = ((( + parlist[i].loc[parPool[0].axis] + parPool[0].offset) * scale) % 2 + ) + heaps = (( + parlist[i].loc[parPool[0].axis] + parPool[0].offset) * scale + ) parPool[0].parity[pair].heap[heaps].parnum += 1 - #print(parPool[0].parity[pair].heap[heaps].parnum) - #printdb(167) - if parPool[0].parity[pair].heap[heaps].parnum > parPool[0].parity[pair].heap[heaps].maxalloc: - #printdb(168) - parPool[0].parity[pair].heap[heaps].maxalloc = (parPool[0].parity[pair].heap[heaps].maxalloc * 1.25) - #printdb(169) - parPool[0].parity[pair].heap[heaps].par = realloc( parPool[0].parity[pair].heap[heaps].par,( parPool[0].parity[pair].heap[heaps].maxalloc + 2 ) * cython.sizeof(int) ) - #printdb(170) - parPool[0].parity[pair].heap[heaps].par[(parPool[0].parity[pair].heap[heaps].parnum - 1)] = parlist[i].id - #printdb(171) - #''' - - #printdb(172) + + if parPool[0].parity[pair].heap[heaps].parnum > \ + parPool[0].parity[pair].heap[heaps].maxalloc: + + parPool[0].parity[pair].heap[heaps].maxalloc = \ + (parPool[0].parity[pair].heap[heaps].maxalloc * 1.25) + + parPool[0].parity[pair].heap[heaps].par = \ + realloc( + parPool[0].parity[pair].heap[heaps].par, + (parPool[0].parity[pair].heap[heaps].maxalloc + 2 ) * \ + cython.sizeof(int) + ) + + parPool[0].parity[pair].heap[heaps].par[ + (parPool[0].parity[pair].heap[heaps].parnum - 1)] = parlist[i].id + if profiling == 1: - print("-->copy data time", clock() - stime,"sec") + print("-->copy data time", clock() - stime, "sec") stime = clock() - - #printdb(173) - KDTree_create_tree(kdtree,parlistcopy,0,parnum - 1,0,-1,0,1) - #printdb(180) + + KDTree_create_tree(kdtree, parlistcopy, 0, parnum - 1, 0, -1, 0, 1) + with nogil: - for i in prange(kdtree.thread_index,schedule='dynamic',chunksize=10,num_threads=cpunum): - KDTree_create_tree(kdtree,parlistcopy,kdtree.thread_start[i],kdtree.thread_end[i],kdtree.thread_name[i],kdtree.thread_parent[i],kdtree.thread_depth[i],0) - + for i in prange( + kdtree.thread_index, + schedule='dynamic', + chunksize=10, + num_threads=cpunum + ): + KDTree_create_tree( + kdtree, + parlistcopy, + kdtree.thread_start[i], + kdtree.thread_end[i], + kdtree.thread_name[i], + kdtree.thread_parent[i], + kdtree.thread_depth[i], + 0 + ) + if profiling == 1: print("-->create tree time", clock() - stime,"sec") stime = clock() - + with nogil: - for i in prange(parnum,schedule='dynamic',chunksize=10,num_threads=cpunum): - KDTree_rnn_query(kdtree,&parlist[i],parlist[i].loc,parlist[i].size * 2) - - #printdb(189) + for i in prange( + parnum, + schedule='dynamic', + chunksize=10, + num_threads=cpunum + ): + KDTree_rnn_query( + kdtree, + &parlist[i], + parlist[i].loc, + parlist[i].size * 2 + ) + if profiling == 1: - print("-->neighbours time", clock() - stime,"sec") + print("-->neighbours time", clock() - stime, "sec") stime = clock() - #''' with nogil: - for pair in xrange(2): - for heaps in prange((parPool[0].max * scale) + 1,schedule='dynamic',chunksize=1,num_threads=cpunum): + for heaps in prange( + (parPool[0].max * scale) + 1, + schedule='dynamic', + chunksize=1, + num_threads=cpunum + ): for i in xrange(parPool[0].parity[pair].heap[heaps].parnum): - collide(&parlist[parPool[0].parity[pair].heap[heaps].par[i]]) - solve_link(&parlist[parPool[0].parity[pair].heap[heaps].par[i]]) - if parlist[parPool[0].parity[pair].heap[heaps].par[i]].neighboursnum > 1: - #printdb(192) - #free(parlist[i].neighbours) - parlist[parPool[0].parity[pair].heap[heaps].par[i]].neighboursnum = 0 - ''' + + collide( + &parlist[parPool[0].parity[pair].heap[heaps].par[i]] + ) + + solve_link( + &parlist[parPool[0].parity[pair].heap[heaps].par[i]] + ) + + if parlist[ + parPool[0].parity[pair].heap[heaps].par[i] + ].neighboursnum > 1: + + # free(parlist[i].neighbours) + + parlist[ + parPool[0].parity[pair].heap[heaps].par[i] + ].neighboursnum = 0 + + ''' with nogil: for i in xrange(parnum): - #printdb(190) collide(&parlist[i]) - #printdb(192) solve_link(&parlist[i]) if parlist[i].neighboursnum > 1: - #printdb(192) #free(parlist[i].neighbours) parlist[i].neighboursnum = 0 - #printdb(194) ''' - + if profiling == 1: - print("-->collide/solve link time", clock() - stime,"sec") + print("-->collide/solve link time", clock() - stime, "sec") stime = clock() - + exportdata = [] parloc = [] parvel = [] parloctmp = [] parveltmp = [] - #printdb(196) + for i in xrange(psysnum): for ii in xrange(psys[i].parnum): parloctmp.append(psys[i].particles[ii].loc[0]) @@ -453,22 +515,26 @@ cpdef simulate(importdata): parveltmp.append(psys[i].particles[ii].vel[0]) parveltmp.append(psys[i].particles[ii].vel[1]) parveltmp.append(psys[i].particles[ii].vel[2]) - parloc.append(parloctmp) + parloc.append(parloctmp) parvel.append(parveltmp) parloctmp = [] parveltmp = [] - #printdb(198) - - #print " New links at this frame: ",newlinks - #print " Broken links this frame: ",deadlinks + totallinks += newlinks pydeadlinks = 0 for i in xrange(cpunum): pydeadlinks += deadlinks[i] totaldeadlinks += pydeadlinks - #print " left: ",totallinks - totaldeadlinks," on ",totallinks - exportdata = [parloc,parvel,newlinks,pydeadlinks,totallinks,totaldeadlinks] - #''' + + exportdata = [ + parloc, + parvel, + newlinks, + pydeadlinks, + totallinks, + totaldeadlinks + ] + for pair in xrange(2): for heaps in range((parPool[0].max * scale) + 1): parPool[0].parity[pair].heap[heaps].parnum = 0 @@ -476,12 +542,13 @@ cpdef simulate(importdata): free(parPool[0].parity[pair].heap) free(parPool[0].parity) free(parPool) - #''' + if profiling == 1: - print("-->export time", clock() - stime,"sec") - print("-->all process time", clock() - stime2,"sec") + print("-->export time", clock() - stime, "sec") + print("-->all process time", clock() - stime2, "sec") return exportdata + cpdef memfree(): global kdtree global psysnum @@ -493,7 +560,7 @@ cpdef memfree(): global substep global deadlinks cdef int i = 0 - #printdb(200) + fps = 0 substep = 0 deltatime = 0 @@ -503,11 +570,11 @@ cpdef memfree(): totaldeadlinks = 0 free(deadlinks) deadlinks = NULL - #printdb(205) + for i in xrange(parnum): if parnum >= 1: - #free(parlist[i].sys) - #parlist[i].sys = NULL + # free(parlist[i].sys) + # parlist[i].sys = NULL if parlist[i].neighboursnum >= 1: free(parlist[i].neighbours) parlist[i].neighbours = NULL @@ -529,26 +596,25 @@ cpdef memfree(): free(parlist[i].neighbours) parlist[i].neighbours = NULL parlist[i].neighboursnum = 0 - #printdb(208) - + for i in xrange(psysnum): if psysnum >= 1: - #free(psys[i].particles) + # free(psys[i].particles) psys[i].particles = NULL - #printdb(210) - + if psysnum >= 1: free(psys) psys = NULL - #printdb(215) + if parnum >= 1: free(parlistcopy) parlistcopy = NULL free(parlist) parlist = NULL - #printdb(220) + parnum = 0 psysnum = 0 + if kdtree.numnodes >= 1: for i in xrange(kdtree.numnodes): free(kdtree.nodes[i].particle) @@ -557,6 +623,7 @@ cpdef memfree(): kdtree.nodes[i].left_child = NULL free(kdtree.nodes[i].right_child) kdtree.nodes[i].right_child = NULL + free(kdtree.thread_nodes) kdtree.thread_nodes = NULL free(kdtree.thread_start) @@ -573,11 +640,11 @@ cpdef memfree(): kdtree.nodes = NULL free(kdtree.root_node) kdtree.root_node = NULL + free(kdtree) kdtree = NULL - - + #@cython.cdivision(True) cdef void collide(Particle *par)nogil: global kdtree @@ -599,12 +666,12 @@ cdef void collide(Particle *par)nogil: cdef float ratio2 = 0 cdef float factor1 = 0 cdef float factor2 = 0 - cdef float *col_normal1 = [0,0,0] - cdef float *col_normal2 = [0,0,0] - cdef float *ypar_vel = [0,0,0] - cdef float *xpar_vel = [0,0,0] - cdef float *yi_vel = [0,0,0] - cdef float *xi_vel = [0,0,0] + cdef float *col_normal1 = [0, 0, 0] + cdef float *col_normal2 = [0, 0, 0] + cdef float *ypar_vel = [0, 0, 0] + cdef float *xpar_vel = [0, 0, 0] + cdef float *yi_vel = [0, 0, 0] + cdef float *xi_vel = [0, 0, 0] cdef float friction1 = 0 cdef float friction2 = 0 cdef float damping1 = 0 @@ -621,59 +688,61 @@ cdef void collide(Particle *par)nogil: cdef float force1 = 0 cdef float force2 = 0 cdef float mathtmp = 0 - + if par.state >= 2: return - if par.sys.selfcollision_active == False and par.sys.othercollision_active == False: + if par.sys.selfcollision_active == False \ + and par.sys.othercollision_active == False: return - #printdb(282) - #neighbours = KDTree_rnn_query(kdtree,par.loc,par.size * 2) + + # neighbours = KDTree_rnn_query(kdtree, par.loc, par.size * 2) neighbours = par.neighbours - #printdb(284) - #for i in xrange(kdtree.num_result): + + # for i in xrange(kdtree.num_result): for i in xrange(par.neighboursnum): check = 0 - #printdb(287) if parlist[i].id == -1: check += 1 - #printdb(290) par2 = &parlist[neighbours[i]] - #printdb(292) if par.id == par2.id: check += 10 - #printdb(295) - if arraysearch(par2.id,par.collided_with,par.collided_num) == -1: - #if par2 not in par.collided_with: - #printdb(298) + if arraysearch(par2.id, par.collided_with, par.collided_num) == -1: + # if par2 not in par.collided_with: if par2.sys.id != par.sys.id : - #printdb(300) - if par2.sys.othercollision_active == False or par.sys.othercollision_active == False: - #printdb(302) + if par2.sys.othercollision_active == False or \ + par.sys.othercollision_active == False: check += 100 + if par2.sys.collision_group != par.sys.collision_group: - #printdb(304) check += 1000 - if par2.sys.id == par.sys.id and par.sys.selfcollision_active == False: - #printdb(308) + + if par2.sys.id == par.sys.id and \ + par.sys.selfcollision_active == False: check += 10000 - #printdb(310) + stiff = deltatime target = (par.size + par2.size) * 0.999 sqtarget = target * target - #printdb(314) - if check == 0 and par2.state <= 1 and arraysearch(par2.id,par.link_with,par.link_withnum) == -1 and arraysearch(par.id,par2.link_with,par2.link_withnum) == -1: - #if par.state <= 1 and par2.state <= 1 and par2 not in par.link_with and par not in par2.link_with: - #printdb(317) + + if check == 0 and par2.state <= 1 and \ + arraysearch( + par2.id, par.link_with, par.link_withnum + ) == -1 and \ + arraysearch( + par.id, par2.link_with, par2.link_withnum + ) == -1: + + # if par.state <= 1 and par2.state <= 1 and \ + # par2 not in par.link_with and par not in par2.link_with: lenghtx = par.loc[0] - par2.loc[0] lenghty = par.loc[1] - par2.loc[1] lenghtz = par.loc[2] - par2.loc[2] - sqlenght = square_dist(par.loc,par2.loc,3) - #printdb(322) + sqlenght = square_dist(par.loc, par2.loc, 3) if sqlenght != 0 and sqlenght < sqtarget: - lenght = sqlenght**0.5 + lenght = sqlenght ** 0.5 invlenght = 1 / lenght factor = (lenght - target) * invlenght - ratio1 = (par2.mass/(par.mass + par2.mass)) + ratio1 = (par2.mass / (par.mass + par2.mass)) ratio2 = 1 - ratio1 mathtmp = factor * stiff @@ -685,9 +754,6 @@ cdef void collide(Particle *par)nogil: par2.vel[0] += lenghtx * force2 par2.vel[1] += lenghty * force2 par2.vel[2] += lenghtz * force2 - - - #printdb(336) col_normal1[0] = (par2.loc[0] - par.loc[0]) * invlenght col_normal1[1] = (par2.loc[1] - par.loc[1]) * invlenght @@ -695,26 +761,25 @@ cdef void collide(Particle *par)nogil: col_normal2[0] = col_normal1[0] * -1 col_normal2[1] = col_normal1[1] * -1 col_normal2[2] = col_normal1[2] * -1 - - factor1 = dot_product(par.vel,col_normal1) - + + factor1 = dot_product(par.vel,col_normal1) + ypar_vel[0] = factor1 * col_normal1[0] ypar_vel[1] = factor1 * col_normal1[1] ypar_vel[2] = factor1 * col_normal1[2] xpar_vel[0] = par.vel[0] - ypar_vel[0] xpar_vel[1] = par.vel[1] - ypar_vel[1] xpar_vel[2] = par.vel[2] - ypar_vel[2] - #printdb(352) - - factor2 = dot_product(par2.vel,col_normal2) - + + factor2 = dot_product(par2.vel, col_normal2) + yi_vel[0] = factor2 * col_normal2[0] yi_vel[1] = factor2 * col_normal2[1] yi_vel[2] = factor2 * col_normal2[2] xi_vel[0] = par2.vel[0] - yi_vel[0] xi_vel[1] = par2.vel[1] - yi_vel[1] xi_vel[2] = par2.vel[2] - yi_vel[2] - + ''' Ua = factor1 Ub = -factor2 @@ -724,10 +789,10 @@ cdef void collide(Particle *par)nogil: Va = (Cr*Mb*(Ub-Ua)+Ma*Ua+Mb*Ub)/(Ma+Mb) Vb = (Cr*Ma*(Ua-Ub)+Ma*Ua+Mb*Ub)/(Ma+Mb) - #mula = 1 - #mulb = 1 - #Va = Va * (1 - Cr) - #Vb = Vb * (1 - Cr) + # mula = 1 + # mulb = 1 + # Va = Va * (1 - Cr) + # Vb = Vb * (1 - Cr) ypar_vel[0] = col_normal1[0] * Va ypar_vel[1] = col_normal1[1] * Va ypar_vel[2] = col_normal1[2] * Va @@ -736,42 +801,64 @@ cdef void collide(Particle *par)nogil: yi_vel[2] = col_normal1[2] * Vb ''' - #printdb(381) - friction1 = 1 - (((par.sys.friction + par2.sys.friction) * 0.5) * ratio1) - friction2 = 1 - (((par.sys.friction + par2.sys.friction) * 0.5) * ratio2) - damping1 = 1 - (((par.sys.collision_damp + par2.sys.collision_damp) * 0.5) * ratio1) - damping2 = 1 - (((par.sys.collision_damp + par2.sys.collision_damp) * 0.5) * ratio2) - #xpar_vel[0] *= friction - #xpar_vel[1] *= friction - #xpar_vel[2] *= friction - #xi_vel[0] *= friction - #xi_vel[1] *= friction - #xi_vel[2] *= friction - - par.vel[0] = ((ypar_vel[0] * damping1) + (yi_vel[0] * (1 - damping1))) + ((xpar_vel[0] * friction1) + ( xi_vel[0] * ( 1 - friction1))) - par.vel[1] = ((ypar_vel[1] * damping1) + (yi_vel[1] * (1 - damping1))) + ((xpar_vel[1] * friction1) + ( xi_vel[1] * ( 1 - friction1))) - par.vel[2] = ((ypar_vel[2] * damping1) + (yi_vel[2] * (1 - damping1))) + ((xpar_vel[2] * friction1) + ( xi_vel[2] * ( 1 - friction1))) - par2.vel[0] = ((yi_vel[0] * damping2) + (ypar_vel[0] * (1 - damping2))) + ((xi_vel[0] * friction2) + ( xpar_vel[0] * ( 1 - friction2))) - par2.vel[1] = ((yi_vel[1] * damping2) + (ypar_vel[1] * (1 - damping2))) + ((xi_vel[1] * friction2) + ( xpar_vel[1] * ( 1 - friction2))) - par2.vel[2] = ((yi_vel[2] * damping2) + (ypar_vel[2] * (1 - damping2))) + ((xi_vel[2] * friction2) + ( xpar_vel[2] * ( 1 - friction2))) - #printdb(396) - + friction1 = 1 - ((( + par.sys.friction + par2.sys.friction) * 0.5) * ratio1 + ) + + friction2 = 1 - ((( + par.sys.friction + par2.sys.friction) * 0.5) * ratio2 + ) + + damping1 = 1 - ((( + par.sys.collision_damp + par2.sys.collision_damp + ) * 0.5) * ratio1) + + damping2 = 1 - ((( + par.sys.collision_damp + par2.sys.collision_damp + ) * 0.5) * ratio2) + + # xpar_vel[0] *= friction + # xpar_vel[1] *= friction + # xpar_vel[2] *= friction + # xi_vel[0] *= friction + # xi_vel[1] *= friction + # xi_vel[2] *= friction + + par.vel[0] = ((ypar_vel[0] * damping1) + (yi_vel[0] * \ + (1 - damping1))) + ((xpar_vel[0] * friction1) + \ + ( xi_vel[0] * ( 1 - friction1))) + + par.vel[1] = ((ypar_vel[1] * damping1) + (yi_vel[1] * \ + (1 - damping1))) + ((xpar_vel[1] * friction1) + \ + ( xi_vel[1] * ( 1 - friction1))) + + par.vel[2] = ((ypar_vel[2] * damping1) + (yi_vel[2] * \ + (1 - damping1))) + ((xpar_vel[2] * friction1) + \ + ( xi_vel[2] * ( 1 - friction1))) + + par2.vel[0] = ((yi_vel[0] * damping2) + (ypar_vel[0] * \ + (1 - damping2))) + ((xi_vel[0] * friction2) + \ + ( xpar_vel[0] * ( 1 - friction2))) + + par2.vel[1] = ((yi_vel[1] * damping2) + (ypar_vel[1] * \ + (1 - damping2))) + ((xi_vel[1] * friction2) + \ + ( xpar_vel[1] * ( 1 - friction2))) + + par2.vel[2] = ((yi_vel[2] * damping2) + (ypar_vel[2] * \ + (1 - damping2))) + ((xi_vel[2] * friction2) + \ + ( xpar_vel[2] * ( 1 - friction2))) par2.collided_with[par2.collided_num] = par.id par2.collided_num += 1 - par2.collided_with = realloc(par2.collided_with,(par2.collided_num + 1) * cython.sizeof(int) ) - if ((par.sys.relink_chance + par2.sys.relink_chance) / 2) > 0: - #printdb(405) - create_link(par.id,par.sys.link_max * 2,par2.id) + par2.collided_with = realloc( + par2.collided_with, + (par2.collided_num + 1) * cython.sizeof(int) + ) - #printdb(416) - #printdb(417) - #printdb(418) - #printdb(419) - #free(neighbours) - #free(par2) - #printdb(422) + if ((par.sys.relink_chance + par2.sys.relink_chance) / 2) \ + > 0: + create_link(par.id,par.sys.link_max * 2, par2.id) cdef void solve_link(Particle *par)nogil: global parlist @@ -784,10 +871,10 @@ cdef void solve_link(Particle *par)nogil: cdef float exp = 0 cdef Particle *par1 = NULL cdef Particle *par2 = NULL - cdef float *Loc1 = [0,0,0] - cdef float *Loc2 = [0,0,0] - cdef float *V1 = [0,0,0] - cdef float *V2 = [0,0,0] + cdef float *Loc1 = [0, 0, 0] + cdef float *Loc2 = [0, 0, 0] + cdef float *V1 = [0, 0, 0] + cdef float *V2 = [0, 0, 0] cdef float LengthX = 0 cdef float LengthY = 0 cdef float LengthZ = 0 @@ -801,23 +888,23 @@ cdef void solve_link(Particle *par)nogil: cdef float ForceX = 0 cdef float ForceY = 0 cdef float ForceZ = 0 - cdef float *Force1 = [0,0,0] - cdef float *Force2 = [0,0,0] + cdef float *Force1 = [0, 0, 0] + cdef float *Force2 = [0, 0, 0] cdef float ratio1 = 0 cdef float ratio2 = 0 cdef int parsearch = 0 cdef int par2search = 0 - cdef float *normal1 = [0,0,0] - cdef float *normal2 = [0,0,0] + cdef float *normal1 = [0, 0, 0] + cdef float *normal2 = [0, 0, 0] cdef float factor1 = 0 cdef float factor2 = 0 cdef float friction1 = 0 cdef float friction2 = 0 - cdef float *ypar1_vel = [0,0,0] - cdef float *xpar1_vel = [0,0,0] - cdef float *ypar2_vel = [0,0,0] - cdef float *xpar2_vel = [0,0,0] - #broken_links = [] + cdef float *ypar1_vel = [0, 0, 0] + cdef float *xpar1_vel = [0, 0, 0] + cdef float *ypar2_vel = [0, 0, 0] + cdef float *xpar2_vel = [0, 0, 0] + # broken_links = [] if par.state >= 2: return for i in xrange(par.links_num): @@ -839,7 +926,7 @@ cdef void solve_link(Particle *par)nogil: LengthX = Loc2[0] - Loc1[0] LengthY = Loc2[1] - Loc1[1] LengthZ = Loc2[2] - Loc1[2] - Length = (LengthX**2 + LengthY**2 + LengthZ**2)**(0.5) + Length = (LengthX ** 2 + LengthY ** 2 + LengthZ ** 2) ** (0.5) if par.links[i].lenght != Length and Length != 0: if par.links[i].lenght > Length: stiff = par.links[i].stiffness * deltatime @@ -852,8 +939,8 @@ cdef void solve_link(Particle *par)nogil: Vx = V2[0] - V1[0] Vy = V2[1] - V1[1] Vz = V2[2] - V1[2] - V = (Vx * LengthX + Vy * LengthY+Vz * LengthZ) / Length - ForceSpring = ((Length - par.links[i].lenght)**(exp)) * stiff + V = (Vx * LengthX + Vy * LengthY + Vz * LengthZ) / Length + ForceSpring = ((Length - par.links[i].lenght) ** (exp)) * stiff ForceDamper = damping * V ForceX = (ForceSpring + ForceDamper) * LengthX / Length ForceY = (ForceSpring + ForceDamper) * LengthY / Length @@ -866,32 +953,39 @@ cdef void solve_link(Particle *par)nogil: Force2[2] = -ForceZ ratio1 = (par2.mass/(par1.mass + par2.mass)) ratio2 = (par1.mass/(par1.mass + par2.mass)) + + if par1.state == 3: #dead particle, correct velocity ratio of alive partner + ratio1 = 0 + ratio2 = 1 + elif par2.state == 3: + ratio1 = 1 + ratio2 = 0 + par1.vel[0] += Force1[0] * ratio1 par1.vel[1] += Force1[1] * ratio1 par1.vel[2] += Force1[2] * ratio1 par2.vel[0] += Force2[0] * ratio2 par2.vel[1] += Force2[1] * ratio2 par2.vel[2] += Force2[2] * ratio2 - + normal1[0] = LengthX / Length normal1[1] = LengthY / Length normal1[2] = LengthZ / Length normal2[0] = normal1[0] * -1 normal2[1] = normal1[1] * -1 normal2[2] = normal1[2] * -1 - - factor1 = dot_product(par1.vel,normal1) - + + factor1 = dot_product(par1.vel, normal1) + ypar1_vel[0] = factor1 * normal1[0] ypar1_vel[1] = factor1 * normal1[1] ypar1_vel[2] = factor1 * normal1[2] xpar1_vel[0] = par1.vel[0] - ypar1_vel[0] xpar1_vel[1] = par1.vel[1] - ypar1_vel[1] xpar1_vel[2] = par1.vel[2] - ypar1_vel[2] - #printdb(352) - - factor2 = dot_product(par2.vel,normal2) - + + factor2 = dot_product(par2.vel, normal2) + ypar2_vel[0] = factor2 * normal2[0] ypar2_vel[1] = factor2 * normal2[1] ypar2_vel[2] = factor2 * normal2[2] @@ -899,42 +993,63 @@ cdef void solve_link(Particle *par)nogil: xpar2_vel[1] = par2.vel[1] - ypar2_vel[1] xpar2_vel[2] = par2.vel[2] - ypar2_vel[2] - - #printdb(381) friction1 = 1 - ((par.links[i].friction) * ratio1) friction2 = 1 - ((par.links[i].friction) * ratio2) - - par1.vel[0] = ypar1_vel[0] + ((xpar1_vel[0] * friction1) + ( xpar2_vel[0] * ( 1 - friction1))) - par1.vel[1] = ypar1_vel[1] + ((xpar1_vel[1] * friction1) + ( xpar2_vel[1] * ( 1 - friction1))) - par1.vel[2] = ypar1_vel[2] + ((xpar1_vel[2] * friction1) + ( xpar2_vel[2] * ( 1 - friction1))) - par2.vel[0] = ypar2_vel[0] + ((xpar2_vel[0] * friction2) + ( xpar1_vel[0] * ( 1 - friction2))) - par2.vel[1] = ypar2_vel[1] + ((xpar2_vel[1] * friction2) + ( xpar1_vel[1] * ( 1 - friction2))) - par2.vel[2] = ypar2_vel[2] + ((xpar2_vel[2] * friction2) + ( xpar1_vel[2] * ( 1 - friction2))) - #printdb(396) - - if Length > (par.links[i].lenght * (1 + par.links[i].ebroken)) or Length < (par.links[i].lenght * (1 - par.links[i].broken)): + par1.vel[0] = ypar1_vel[0] + ((xpar1_vel[0] * friction1) + \ + (xpar2_vel[0] * ( 1 - friction1))) + + par1.vel[1] = ypar1_vel[1] + ((xpar1_vel[1] * friction1) + \ + (xpar2_vel[1] * ( 1 - friction1))) + + par1.vel[2] = ypar1_vel[2] + ((xpar1_vel[2] * friction1) + \ + (xpar2_vel[2] * ( 1 - friction1))) + + par2.vel[0] = ypar2_vel[0] + ((xpar2_vel[0] * friction2) + \ + (xpar1_vel[0] * ( 1 - friction2))) + + par2.vel[1] = ypar2_vel[1] + ((xpar2_vel[1] * friction2) + \ + (xpar1_vel[1] * ( 1 - friction2))) + + par2.vel[2] = ypar2_vel[2] + ((xpar2_vel[2] * friction2) + \ + (xpar1_vel[2] * ( 1 - friction2))) + + if Length > (par.links[i].lenght * (1 + par.links[i].ebroken)) \ + or Length < (par.links[i].lenght * (1 - par.links[i].broken)): + par.links[i].start = -1 par.links_activnum -= 1 deadlinks[threadid()] += 1 - parsearch = arraysearch(par2.id,par.link_with,par.link_withnum) + + parsearch = arraysearch( + par2.id, + par.link_with, + par.link_withnum + ) + if parsearch != -1: par.link_with[parsearch] = -1 - par2search = arraysearch(par.id,par2.link_with,par2.link_withnum) + + par2search = arraysearch( + par.id, + par2.link_with, + par2.link_withnum + ) + if par2search != -1: par2.link_with[par2search] = -1 - #broken_links.append(link) - #if par2 in par1.link_with: - #par1.link_with.remove(par2) - #if par1 in par2.link_with: - #par2.link_with.remove(par1) - - #par.links = list(set(par.links) - set(broken_links)) - - #free(par1) - #free(par2) - - + + # broken_links.append(link) + # if par2 in par1.link_with: + # par1.link_with.remove(par2) + # if par1 in par2.link_with: + # par2.link_with.remove(par1) + + # par.links = list(set(par.links) - set(broken_links)) + # free(par1) + # free(par2) + + cdef void update(data): global parlist global parnum @@ -950,109 +1065,136 @@ cdef void update(data): psys[i].particles[ii].vel[0] = data[i][1][(ii * 3)] psys[i].particles[ii].vel[1] = data[i][1][(ii * 3) + 1] psys[i].particles[ii].vel[2] = data[i][1][(ii * 3) + 2] + if psys[i].particles[ii].state == 0 and data[i][2][ii] == 0: psys[i].particles[ii].state = data[i][2][ii] + 1 - #printdb(546) if psys[i].links_active == 1: - KDTree_rnn_query(kdtree,&psys[i].particles[ii],psys[i].particles[ii].loc,psys[i].particles[ii].sys.link_length) - create_link(psys[i].particles[ii].id,psys[i].link_max) - #free(psys[i].particles[ii].neighbours) + KDTree_rnn_query( + kdtree, + &psys[i].particles[ii], + psys[i].particles[ii].loc, + psys[i].particles[ii].sys.link_length + ) + create_link(psys[i].particles[ii].id, psys[i].link_max) + # free(psys[i].particles[ii].neighbours) psys[i].particles[ii].neighboursnum = 0 - #printdb(548) elif psys[i].particles[ii].state == 1 and data[i][2][ii] == 0: psys[i].particles[ii].state = 1 else: psys[i].particles[ii].state = data[i][2][ii] - psys[i].particles[ii].collided_with = realloc(psys[i].particles[ii].collided_with, 1 * cython.sizeof(int) ) + + psys[i].particles[ii].collided_with = realloc( + psys[i].particles[ii].collided_with, + 1 * cython.sizeof(int) + ) psys[i].particles[ii].collided_num = 0 - #printdb(558) - + cdef void KDTree_create_nodes(KDTree *kdtree,int parnum):#nogil: cdef int i = 0 i = 2 - #print("create nodes parnum:",parnum) while i < parnum: i = i * 2 - #print("i:",i) kdtree.numnodes = i - #print("numnodes:",kdtree.numnodes) - kdtree.nodes = malloc( (kdtree.numnodes + 1) * cython.sizeof(Node) ) - kdtree.root_node = malloc( 1 * cython.sizeof(Node) ) + kdtree.nodes = malloc((kdtree.numnodes + 1) * cython.sizeof(Node)) + kdtree.root_node = malloc(1 * cython.sizeof(Node)) + for i in xrange(kdtree.numnodes): kdtree.nodes[i].index = i kdtree.nodes[i].name = -1 kdtree.nodes[i].parent = -1 - kdtree.nodes[i].particle = malloc( 1 * cython.sizeof(SParticle) ) - kdtree.nodes[i].left_child = malloc( 1 * cython.sizeof(Node) ) - kdtree.nodes[i].right_child = malloc( 1 * cython.sizeof(Node) ) + + kdtree.nodes[i].particle = malloc( + 1 * cython.sizeof(SParticle) + ) + + kdtree.nodes[i].left_child = malloc(1 * cython.sizeof(Node)) + kdtree.nodes[i].right_child = malloc(1 * cython.sizeof(Node)) kdtree.nodes[i].left_child[0].index = -1 kdtree.nodes[i].right_child[0].index = -1 + kdtree.nodes[kdtree.numnodes].index = -1 kdtree.nodes[kdtree.numnodes].name = -1 kdtree.nodes[kdtree.numnodes].parent = -1 - kdtree.nodes[kdtree.numnodes].particle = malloc( 1 * cython.sizeof(SParticle) ) - kdtree.nodes[kdtree.numnodes].left_child = malloc( 1 * cython.sizeof(Node) ) - kdtree.nodes[kdtree.numnodes].right_child = malloc( 1 * cython.sizeof(Node) ) + + kdtree.nodes[kdtree.numnodes].particle = malloc( + 1 * cython.sizeof(SParticle) + ) + + kdtree.nodes[kdtree.numnodes].left_child = malloc( + 1 * cython.sizeof(Node) + ) + + kdtree.nodes[kdtree.numnodes].right_child = malloc( + 1 * cython.sizeof(Node) + ) + kdtree.nodes[kdtree.numnodes].left_child[0].index = -1 kdtree.nodes[kdtree.numnodes].right_child[0].index = -1 - kdtree.thread_nodes = malloc( 128 * cython.sizeof(int) ) - kdtree.thread_start = malloc( 128 * cython.sizeof(int) ) - kdtree.thread_end = malloc( 128 * cython.sizeof(int) ) - kdtree.thread_name = malloc( 128 * cython.sizeof(int) ) - kdtree.thread_parent = malloc( 128 * cython.sizeof(int) ) - kdtree.thread_depth = malloc( 128 * cython.sizeof(int) ) - #kdtree.axis = malloc( 64 * cython.sizeof(int) ) + kdtree.thread_nodes = malloc(128 * cython.sizeof(int)) + kdtree.thread_start = malloc(128 * cython.sizeof(int)) + kdtree.thread_end = malloc(128 * cython.sizeof(int)) + kdtree.thread_name = malloc(128 * cython.sizeof(int)) + kdtree.thread_parent = malloc(128 * cython.sizeof(int)) + kdtree.thread_depth = malloc(128 * cython.sizeof(int)) + # kdtree.axis = malloc( 64 * cython.sizeof(int) ) + for i in xrange(64): kdtree.axis[i] = i % 3 + return -cdef Node KDTree_create_tree(KDTree *kdtree,SParticle *kdparlist,int start,int end,int name,int parent,int depth,int initiate)nogil: +cdef Node KDTree_create_tree( + KDTree *kdtree, + SParticle *kdparlist, + int start, + int end, + int name, + int parent, + int depth, + int initiate + )nogil: + global parnum cdef int index = 0 cdef int len = (end - start) + 1 - #print("len:",len) if len <= 0: - #print("num nodes len 0",kdtree.numnodes) return kdtree.nodes[kdtree.numnodes] cdef int axis cdef int k = 3 axis = kdtree.axis[depth] - #depth % k - #printdb(590) - quick_sort(kdparlist + start,len,axis) + # depth % k + quick_sort(kdparlist + start, len, axis) ''' if axis == 0: - qsort(kdparlist + start,len,sizeof(SParticle),compare_x) + qsort(kdparlist + start, len, sizeof(SParticle), compare_x) elif axis == 1: - qsort(kdparlist + start,len,sizeof(SParticle),compare_y) + qsort(kdparlist + start, len, sizeof(SParticle), compare_y) elif axis == 2: - qsort(kdparlist + start,len,sizeof(SParticle),compare_z) + qsort(kdparlist + start, len, sizeof(SParticle), compare_z) ''' cdef int median = (start + end) / 2 - #printdb(598) + if depth == 0: - kdtree.thread_index = 0 - index = 0 + kdtree.thread_index = 0 + index = 0 else: index = (parent * 2) + name + if index > kdtree.numnodes: return kdtree.nodes[kdtree.numnodes] - #printdb(605) + kdtree.nodes[index].name = name kdtree.nodes[index].parent = parent - #printdb(607) + if len >= 1 and depth == 0: kdtree.root_node[0] = kdtree.nodes[0] - #printdb(610) - #print("index",index) - #print("num nodes",kdtree.numnodes) - #print("median",median) + kdtree.nodes[index].particle[0] = kdparlist[median] - #printdb(612) + if parnum > 127: if depth == 4 and initiate == 1: kdtree.thread_nodes[kdtree.thread_index] = index @@ -1061,86 +1203,154 @@ cdef Node KDTree_create_tree(KDTree *kdtree,SParticle *kdparlist,int start,int e kdtree.thread_name[kdtree.thread_index] = name kdtree.thread_parent[kdtree.thread_index] = parent kdtree.thread_depth[kdtree.thread_index] = depth - #print(kdtree.nodes[index].index) kdtree.thread_index += 1 return kdtree.nodes[index] - kdtree.nodes[index].left_child[0] = KDTree_create_tree(kdtree,kdparlist,start,median - 1,1,index,depth + 1,initiate) - #printdb(614) - kdtree.nodes[index].right_child[0] = KDTree_create_tree(kdtree,kdparlist,median + 1,end,2,index,depth + 1,initiate) - #printdb(616) + + kdtree.nodes[index].left_child[0] = KDTree_create_tree( + kdtree, + kdparlist, + start, + median - 1, + 1, + index, + depth + 1, + initiate + ) + kdtree.nodes[index].right_child[0] = KDTree_create_tree( + kdtree, + kdparlist, + median + 1, + end, + 2, + index, + depth + 1, + initiate + ) + return kdtree.nodes[index] -cdef int KDTree_rnn_query(KDTree *kdtree,Particle *par,float point[3],float dist)nogil: +cdef int KDTree_rnn_query( + KDTree *kdtree, + Particle *par, + float point[3], + float dist + )nogil: + global parlist cdef float sqdist = 0 cdef int k = 0 cdef int i = 0 par.neighboursnum = 0 - #printdb(639) - #free(par.neighbours) - #printdb(641) - #printdb(643) + # free(par.neighbours) par.neighbours[0] = -1 - #printdb(645) + if kdtree.root_node[0].index != kdtree.nodes[0].index: par.neighbours[0] = -1 par.neighboursnum = 0 return -1 else: sqdist = dist * dist - KDTree_rnn_search(kdtree,&par[0],kdtree.root_node[0],point,dist,sqdist,3,0) + KDTree_rnn_search( + kdtree, &par[0], + kdtree.root_node[0], + point, + dist, + sqdist, + 3, + 0 + ) #@cython.cdivision(True) -cdef void KDTree_rnn_search(KDTree *kdtree,Particle *par,Node node,float point[3],float dist,float sqdist,int k,int depth)nogil: +cdef void KDTree_rnn_search( + KDTree *kdtree, + Particle *par, + Node node, + float point[3], + float dist, + float sqdist, + int k, + int depth + )nogil: + cdef int axis = 0 cdef float realsqdist = 0 - #printdb(642) + if node.index == -1: return - + cdef SParticle tparticle = node.particle[0] - + axis = kdtree.axis[depth] - #depth % k - #printdb(649) + if (fabs(point[axis] - tparticle.loc[axis])) <= dist: - realsqdist = square_dist(point,tparticle.loc,3) - #printdb(652) + realsqdist = square_dist(point, tparticle.loc, 3) + if realsqdist <= sqdist: - #printdb(654) + par.neighbours[par.neighboursnum] = node.particle[0].id par.neighboursnum += 1 if (par.neighboursnum) >= par.neighboursmax: par.neighboursmax = par.neighboursmax * 2 - par.neighbours = realloc(par.neighbours,(par.neighboursmax) * cython.sizeof(int) ) - #printdb(658) - - #printdb(660) - KDTree_rnn_search(kdtree,&par[0],node.left_child[0],point,dist,sqdist,3,depth + 1) - #printdb(662) - KDTree_rnn_search(kdtree,&par[0],node.right_child[0],point,dist,sqdist,3,depth + 1) - #printdb(664) + par.neighbours = realloc( + par.neighbours, + (par.neighboursmax) * cython.sizeof(int) + ) + + KDTree_rnn_search( + kdtree, + &par[0], + node.left_child[0], + point, + dist, + sqdist, + 3, + depth + 1 + ) + + KDTree_rnn_search( + kdtree, + &par[0], + node.right_child[0], + point, + dist, + sqdist, + 3, + depth + 1 + ) + else: if point[axis] <= tparticle.loc[axis]: - #printdb(667) - KDTree_rnn_search(kdtree,&par[0],node.left_child[0],point,dist,sqdist,3,depth + 1) + KDTree_rnn_search( + kdtree, + &par[0], + node.left_child[0], + point, + dist, + sqdist, + 3, + depth + 1 + ) + if point[axis] >= tparticle.loc[axis]: - #printdb(670) - KDTree_rnn_search(kdtree,&par[0],node.right_child[0],point,dist,sqdist,3,depth + 1) - #printdb(672) - - -cdef void create_link(int par_id, int max_link, int parothers_id = -1)nogil: - #printdb(676) + KDTree_rnn_search( + kdtree, + &par[0], + node.right_child[0], + point, + dist, + sqdist, + 3, + depth + 1 + ) + +cdef void create_link(int par_id, int max_link, int parothers_id=-1)nogil: global kdtree global parlist global parnum global newlinks - #printdb(680) - cdef Links *link = malloc( 1 * cython.sizeof(Links)) - #printdb(682) + cdef Links *link = malloc(1 * cython.sizeof(Links)) cdef int *neighbours = NULL cdef int ii = 0 cdef int neighboursnum = 0 @@ -1155,33 +1365,30 @@ cdef void create_link(int par_id, int max_link, int parothers_id = -1)nogil: cdef float tensionrandom = 0 cdef float chancerdom = 0 cdef Particle *fakepar = NULL - fakepar = malloc( 1 * cython.sizeof(Particle)) + cdef int create_links + fakepar = malloc(1 * cython.sizeof(Particle)) par = &parlist[par_id] - #printdb(693) + if par.state >= 2: return if par.links_activnum >= max_link: return if par.sys.links_active == 0: - #printdb(699) return - #printdb(705) + if parothers_id == -1: - #printdb(707) - #KDTree_rnn_query(kdtree,&fakepar[0],par.loc,par.sys.link_length) - #neighbours = fakepar[0].neighbours + # KDTree_rnn_query(kdtree, &fakepar[0], par.loc, par.sys.link_length) + # neighbours = fakepar[0].neighbours neighbours = par.neighbours neighboursnum = par.neighboursnum - #printdb(709) else: - #printdb(711) - neighbours = malloc( 1 * cython.sizeof(int)) + neighbours = malloc(1 * cython.sizeof(int)) neighbours[0] = parothers_id neighboursnum = 1 - - #printdb(714) + for ii in xrange(neighboursnum): - #printdb(720) + if par.links_activnum >= max_link: + break if parothers_id == -1: par2 = &parlist[neighbours[ii]] tension = (par.sys.link_tension + par2.sys.link_tension) / 2 @@ -1189,66 +1396,74 @@ cdef void create_link(int par_id, int max_link, int parothers_id = -1)nogil: par2 = &parlist[neighbours[0]] tension = (par.sys.link_tension + par2.sys.link_tension) / 2 if par.id != par2.id: - #printdb(723) - #arraysearch(par2.id,par.link_with,par.link_withnum) - #printdb(725) - if arraysearch(par.id,par2.link_with,par2.link_withnum) == -1 and par2.state <= 1 and par.state <= 1: - #if par not in par2.link_with and par2.state <= 1 and par.state <= 1: - #printdb(728) - #printdb(729) + # arraysearch(par2.id, par.link_with, par.link_withnum) + + if arraysearch(par.id,par2.link_with,par2.link_withnum) == -1 and \ + par2.state <= 1 and par.state <= 1: + + #if par not in par2.link_with and par2.state <= 1 \ + # and par.state <= 1: + link.start = par.id link.end = par2.id - #printdb(732) - link.friction = (par.sys.link_friction + par2.sys.link_friction) / 2 - if parothers_id == -1: - tensionrandom = (par.sys.link_tensionrand + par2.sys.link_tensionrand) / 2 * 2 - srand(1) - tension = ((par.sys.link_tension + par2.sys.link_tension)/2) * ((((rand() / rand_max) * tensionrandom) - (tensionrandom / 2)) + 1) - srand(2) - link.lenght = ((square_dist(par.loc,par2.loc,3))**0.5) * tension - stiffrandom = (par.sys.link_stiffrand + par2.sys.link_stiffrand) / 2 * 2 - link.stiffness = ((par.sys.link_stiff + par2.sys.link_stiff)/2) * ((((rand() / rand_max) * stiffrandom) - (stiffrandom / 2)) + 1) - srand(3) - link.estiffness = ((par.sys.link_estiff + par2.sys.link_estiff)/2) * ((((rand() / rand_max) * stiffrandom) - (stiffrandom / 2)) + 1) - srand(4) - link.exponent = abs(int((par.sys.link_stiffexp + par2.sys.link_stiffexp) / 2))#### - link.eexponent = abs(int((par.sys.link_estiffexp + par2.sys.link_estiffexp) / 2))#### - damprandom = ((par.sys.link_damprand + par2.sys.link_damprand) / 2) * 2 - link.damping = ((par.sys.link_damp + par2.sys.link_damp) / 2) * ((((rand() / rand_max) * damprandom) - (damprandom / 2)) + 1) - srand(5) - link.edamping = ((par.sys.link_edamp + par2.sys.link_edamp) / 2) * ((((rand() / rand_max) * damprandom) - (damprandom / 2)) + 1) - brokrandom = ((par.sys.link_brokenrand + par2.sys.link_brokenrand) / 2) * 2 - srand(6) - link.broken = ((par.sys.link_broken + par2.sys.link_broken) / 2) * ((((rand() / rand_max) * brokrandom) - (brokrandom / 2)) + 1) - srand(7) - link.ebroken = ((par.sys.link_ebroken + par2.sys.link_ebroken) / 2) * ((((rand() / rand_max) * brokrandom) - (brokrandom / 2)) + 1) - #printdb(748) - par.links[par.links_num] = link[0] - par.links_num += 1 - par.links_activnum += 1 - #printdb(752) - par.links = realloc(par.links,(par.links_num + 2) * cython.sizeof(Links) ) - - #printdb(755) - par.link_with[par.link_withnum] = par2.id - par.link_withnum += 1 - #printdb(758) - par.link_with = realloc(par.link_with,(par.link_withnum + 2) * cython.sizeof(int) ) - - par2.link_with[par2.link_withnum] = par.id - par2.link_withnum += 1 - #printdb(763) - par2.link_with = realloc(par2.link_with,(par2.link_withnum + 2) * cython.sizeof(int) ) - newlinks += 1 - #free(link) - #printdb(766) - + + link.friction = ( + par.sys.link_friction + par2.sys.link_friction) / 2 + + if parothers_id == -1 and par.sys.link_group == par2.sys.link_group: + if par.sys.id != par2.sys.id: + if par.sys.other_link_active and par2.sys.other_link_active: + create_links = 1 + else: + create_links = 0 + else: + create_links = 1 + + if create_links == 1: + tensionrandom = (par.sys.link_tensionrand + par2.sys.link_tensionrand) / 2 * 2 + srand(1) + tension = ((par.sys.link_tension + par2.sys.link_tension)/2) * ((((rand() / rand_max) * tensionrandom) - (tensionrandom / 2)) + 1) + srand(2) + link.lenght = ((square_dist(par.loc,par2.loc,3))**0.5) * tension + stiffrandom = (par.sys.link_stiffrand + par2.sys.link_stiffrand) / 2 * 2 + link.stiffness = ((par.sys.link_stiff + par2.sys.link_stiff)/2) * ((((rand() / rand_max) * stiffrandom) - (stiffrandom / 2)) + 1) + srand(3) + link.estiffness = ((par.sys.link_estiff + par2.sys.link_estiff)/2) * ((((rand() / rand_max) * stiffrandom) - (stiffrandom / 2)) + 1) + srand(4) + link.exponent = abs(int((par.sys.link_stiffexp + par2.sys.link_stiffexp) / 2)) + link.eexponent = abs(int((par.sys.link_estiffexp + par2.sys.link_estiffexp) / 2)) + damprandom = ((par.sys.link_damprand + par2.sys.link_damprand) / 2) * 2 + link.damping = ((par.sys.link_damp + par2.sys.link_damp) / 2) * ((((rand() / rand_max) * damprandom) - (damprandom / 2)) + 1) + srand(5) + link.edamping = ((par.sys.link_edamp + par2.sys.link_edamp) / 2) * ((((rand() / rand_max) * damprandom) - (damprandom / 2)) + 1) + brokrandom = ((par.sys.link_brokenrand + par2.sys.link_brokenrand) / 2) * 2 + srand(6) + link.broken = ((par.sys.link_broken + par2.sys.link_broken) / 2) * ((((rand() / rand_max) * brokrandom) - (brokrandom / 2)) + 1) + srand(7) + link.ebroken = ((par.sys.link_ebroken + par2.sys.link_ebroken) / 2) * ((((rand() / rand_max) * brokrandom) - (brokrandom / 2)) + 1) + par.links[par.links_num] = link[0] + par.links_num += 1 + par.links_activnum += 1 + par.links = realloc(par.links,(par.links_num + 2) * cython.sizeof(Links)) + + par.link_with[par.link_withnum] = par2.id + par.link_withnum += 1 + + par.link_with = realloc(par.link_with,(par.link_withnum + 2) * cython.sizeof(int)) + + par2.link_with[par2.link_withnum] = par.id + par2.link_withnum += 1 + + par2.link_with = realloc(par2.link_with,(par2.link_withnum + 2) * cython.sizeof(int)) + newlinks += 1 + # free(link) + if parothers_id != -1 and par.sys.relink_group == par2.sys.relink_group: - #printdb(769) srand(8) relinkrandom = (rand() / rand_max) chancerdom = (par.sys.relink_chancerand + par2.sys.relink_chancerand) / 2 * 2 srand(9) + if relinkrandom <= ((par.sys.relink_chance + par2.sys.relink_chance) / 2) * ((((rand() / rand_max) * chancerdom) - (chancerdom / 2)) + 1): tensionrandom = (par.sys.relink_tensionrand + par2.sys.relink_tensionrand) / 2 * 2 srand(10) @@ -1260,8 +1475,8 @@ cdef void create_link(int par_id, int max_link, int parothers_id = -1)nogil: srand(12) link.estiffness = ((par.sys.relink_estiff + par2.sys.relink_estiff)/2) * ((((rand() / rand_max) * stiffrandom) - (stiffrandom / 2)) + 1) srand(13) - link.exponent = abs(int((par.sys.relink_stiffexp + par2.sys.relink_stiffexp) / 2))#### - link.eexponent = abs(int((par.sys.relink_estiffexp + par2.sys.relink_estiffexp) / 2))#### + link.exponent = abs(int((par.sys.relink_stiffexp + par2.sys.relink_stiffexp) / 2)) + link.eexponent = abs(int((par.sys.relink_estiffexp + par2.sys.relink_estiffexp) / 2)) damprandom = ((par.sys.relink_damprand + par2.sys.relink_damprand) / 2) * 2 link.damping = ((par.sys.relink_damp + par2.sys.relink_damp) / 2) * ((((rand() / rand_max) * damprandom) - (damprandom / 2)) + 1) srand(14) @@ -1273,21 +1488,22 @@ cdef void create_link(int par_id, int max_link, int parothers_id = -1)nogil: par.links[par.links_num] = link[0] par.links_num += 1 par.links_activnum += 1 - par.links = realloc(par.links,(par.links_num + 1) * cython.sizeof(Links) ) + par.links = realloc(par.links,(par.links_num + 1) * cython.sizeof(Links)) par.link_with[par.link_withnum] = par2.id par.link_withnum += 1 - par.link_with = realloc(par.link_with,(par.link_withnum + 1) * cython.sizeof(int) ) + par.link_with = realloc(par.link_with,(par.link_withnum + 1) * cython.sizeof(int)) par2.link_with[par2.link_withnum] = par.id par2.link_withnum += 1 - par2.link_with = realloc(par2.link_with,(par2.link_withnum + 1) * cython.sizeof(int) ) + par2.link_with = realloc(par2.link_with,(par2.link_withnum + 1) * cython.sizeof(int)) newlinks += 1 - #free(link) - #free(neighbours) - #free(fakepar) - #free(link) - #free(par) - #free(par2) - + # free(link) + # free(neighbours) + free(fakepar) + free(link) + # free(par) + # free(par2) + + cdef struct Links: float lenght int start @@ -1301,13 +1517,12 @@ cdef struct Links: float edamping float ebroken float friction - - - + + cdef struct KDTree: int numnodes - #int num_result - #int *result + # int num_result + # int *result Node *root_node Node *nodes char axis[64] @@ -1328,7 +1543,7 @@ cdef struct Node: SParticle *particle Node *left_child Node *right_child - + cdef struct ParSys: int id @@ -1379,11 +1594,15 @@ cdef struct ParSys: float relink_ebroken float relink_ebrokenrand float link_friction + int link_group + int other_link_active + cdef struct SParticle: int id float loc[3] - + + cdef struct Particle: int id float loc[3] @@ -1403,15 +1622,18 @@ cdef struct Particle: int neighboursnum int neighboursmax + cdef struct Pool: char axis float offset float max Parity *parity - + + cdef struct Parity: Heap *heap - + + cdef struct Heap: int *par int parnum @@ -1425,7 +1647,8 @@ cdef int compare_x (const void *u, const void *v)nogil: if w > 0: return 1 return 0 - + + cdef int compare_y (const void *u, const void *v)nogil: cdef float w = (u).loc[1] - (v).loc[1] if w < 0: @@ -1433,8 +1656,8 @@ cdef int compare_y (const void *u, const void *v)nogil: if w > 0: return 1 return 0 - - + + cdef int compare_z (const void *u, const void *v)nogil: cdef float w = (u).loc[2] - (v).loc[2] if w < 0: @@ -1442,7 +1665,8 @@ cdef int compare_z (const void *u, const void *v)nogil: if w > 0: return 1 return 0 - + + cdef int compare_id (const void *u, const void *v)nogil: cdef float w = (u).id - (v).id if w < 0: @@ -1451,23 +1675,23 @@ cdef int compare_id (const void *u, const void *v)nogil: return 1 return 0 - -cdef int arraysearch(int element,int *array,int len)nogil: + +cdef int arraysearch(int element, int *array, int len)nogil: cdef int i = 0 - #printdb(939) for i in xrange(len): if element == array[i]: return i - #printdb(943) return -1 - + + cdef float fabs(float value)nogil: if value >= 0: return value if value < 0: return -value - -cdef float sq_number(float val):# nogil: + + +cdef float sq_number(float val): cdef float nearsq = 8 while val > nearsq or val < nearsq / 2: if val > nearsq: @@ -1475,22 +1699,23 @@ cdef float sq_number(float val):# nogil: elif val < nearsq / 2: nearsq = nearsq / 2 return nearsq - -#@cython.cdivision(True) -cdef float square_dist(float point1[3],float point2[3],int k)nogil: + + +#@cython.cdivision(True) +cdef float square_dist(float point1[3], float point2[3], int k)nogil: cdef float sq_dist = 0 for i in xrange(k): sq_dist += (point1[i] - point2[i]) * (point1[i] - point2[i]) return sq_dist - + cdef float dot_product(float u[3],float v[3])nogil: cdef float dot = 0 dot = (u[0] * v[0]) + (u[1] * v[1]) + (u[2] * v[2]) return dot -cdef void quick_sort (SParticle *a, int n, int axis)nogil: +cdef void quick_sort(SParticle *a, int n, int axis)nogil: if (n < 2): return cdef SParticle t @@ -1504,14 +1729,17 @@ cdef void quick_sort (SParticle *a, int n, int axis)nogil: if r[0].loc[axis] > p: r -= 1 - continue #// we need to check the condition (l <= r) every time we change the value of l or r - + # // we need to check the condition (l <= r) every time + # we change the value of l or r + continue + t = l[0] l[0] = r[0] - #l[0], r[0] = r[0], l[0] # suggested by stephan to remove temp variable t but slower + # suggested by stephan to remove temp variable t but slower + # l[0], r[0] = r[0], l[0] l += 1 r[0] = t r -= 1 - - quick_sort(a, r - a + 1,axis) - quick_sort(l, a + n - l,axis) + + quick_sort(a, r - a + 1, axis) + quick_sort(l, a + n - l, axis) diff --git a/sources/setup.bat b/sources/setup.bat new file mode 100644 index 0000000..dac1fe7 --- /dev/null +++ b/sources/setup.bat @@ -0,0 +1,18 @@ +rem delete old files +del core.html +del core.c +rd /s /q build +del core.cp37-win_amd64.pyd + +rem compiling +"C:\Program Project\Python 3.7.7\python.exe" setup.py build_ext --inplace + +rem delete unnecessary files after compilation +del core.html +del core.c +rd /s /q build + +rem move the core to the blender addon folder +move core.cp37-win_amd64.pyd ..\molecular\core.cp37-win_amd64.pyd + +pause diff --git a/sources/setup.py b/sources/setup.py new file mode 100644 index 0000000..6b09d80 --- /dev/null +++ b/sources/setup.py @@ -0,0 +1,43 @@ +import platform +from distutils.core import setup +from Cython.Distutils import build_ext, Extension +import Cython.Compiler.Options + + +os_name = platform.architecture()[1] +Cython.Compiler.Options.annotate = True +module_name = 'core' +is_linux = platform.architecture()[1] == "ELF" or platform.system() == "Linux" + +if os_name == "WindowsPE": + ext_modules = [Extension( + module_name, + ['core' + '.pyx'], + extra_compile_args=['/Ox','/openmp','/GT','/arch:SSE2','/fp:fast'], + cython_directives={'language_level' : "3", 'cpow': True} + )] +elif is_linux: + ext_modules = [Extension( + module_name, + ['core' + '.pyx'], + extra_compile_args=['-O3','-msse4.2','-ffast-math','-fno-builtin'], + extra_link_args=['-lm'], + cython_directives={'language_level' : "3", 'cpow': True} + )] +else: + # under mac OS, with the advent of the M1 ARM chips, its necessary to build an universal intel/arm binary. + # this is done by passing -arch arm64 -arch x86_64 from here to clang, the default mac OS compiler. + + ext_modules = [Extension( + module_name, + ['core' + '.pyx'], + extra_compile_args=['-O3','-msse4.2','-ffast-math','-fno-builtin','-arch','arm64e','-arch','x86_64','-arch','arm64'], + extra_link_args=['-lm','-arch','arm64e','-arch','x86_64','-arch','arm64'], + cython_directives={'language_level' : "3", 'cpow': True} + )] + +setup( + name = 'Molecular script', + cmdclass = {'build_ext': build_ext}, + ext_modules = ext_modules +)