Jump to content
Woods

Plugin for woodFill or similar filament

Recommended Posts

Posted · Plugin for woodFill or similar filament

Does anyone know if there is a plugin for Cura (I'm currently using 2.1.2) that can automate a random heat change pattern, resulting in a colour change when using woodFill or a similar filament? I know there is the TweakAtZ 5.0.1 script, but this can be a bit laborious when you are printing a tall object and you want the colour to change every millimetre or so.

Share this post


Link to post
Share on other sites
Posted · Plugin for woodFill or similar filament

Check out this item on Thingiverse. The author has a standalone python script that it looks like you just feed it your .gcode file

Generate wood patterns with temperature changes

This might work as-is in Cura 2.x, I dunno

 

Thanks for that DaHai8,

I had already found that, but can't work out how to plug it in to the Cura software. So if anyone can help with that I'd appreciate it.

Share this post


Link to post
Share on other sites
Posted · Plugin for woodFill or similar filament

Hmm...I took a close look at it and won't work as is in Cura 2.x, but...I think I can convert it. Gonna give it a shot and let you know.

Share this post


Link to post
Share on other sites
Posted · Plugin for woodFill or similar filament

Almost ready...I think.

Ran into a detour when I saw Cura 2.3 was out and took a while to discover/fix the plugins issues I was having with that new version.

Share this post


Link to post
Share on other sites
Posted (edited) · Plugin for woodFill or similar filament

Any hint´s what to change in the plugins - or do they have to be rewritten completely?

Edited by Guest

Share this post


Link to post
Share on other sites
Posted · Plugin for woodFill or similar filament

This one had to be rehashed from top/bottom because it tried to be a 15.x plugin and a stand-alone python script at the same time.

There are new defs in Cura 2.x like "getSettingDataString(self)" to get the user's input, as well as 15.x handled the .gcode file directly whereas in 2.x, the plugin is handed a structure containing the gcode, so no file reads/writes.

There are also some Python2 v. new Python3 differences that caught me a few times.

However, as for Cura 2.1.x v 2.3 the changes are minor (which is why I kept missing them!):

1. getSettingData appears to be deprecated and replaced with getSettingDataString

2. "default" is now "default_value" in the settings

3. "visible" is deprecated and cannot be used

4. "name", "key", "metadata", "version" in the getSettingDataString appear to be required

5. "settings" is also required, but it can be empty brackets

6. You can return the Setting Data as a big string (or not - not sure which way is preferred):

 

def getSettingDataString(self):       return """{           "name":"Layer Numbers",           "key": "LayerNumbers",           "metadata":{},           "version": 2,           "settings":           {           }       }"""

 

Everything else seems to work without modifications. So its all apparently confined to the getSettingDataString function/def/thingy

Share this post


Link to post
Share on other sites
Posted · Plugin for woodFill or similar filament

Wood Grain Post Processing Plugin for Cura 2.3 (Beta) is done!

I'm don't think it will work in 2.1.x...

Here's the code WoodGrain.py (no warranty whatsoever, use at your own risk, etc., etc., blah, blah, blah...)

 

#Name: Wood Grain#Info: Vary the print temperature troughout the print to create wood rings with some printing material such as the LayWoo. The higher the temperature, the darker the print.#Depend: GCode#Type: postprocess#Param: minTemp(float:180) Mininmum print temperature (degree C)#Param: maxTemp(float:230) Maximun print temperature (degree C)#Param: grainSize(float:3.0) Average "wood grain" size (mm)#Param: firstTemp(float:0) Starting temperature (degree C, zero to disable)#Param: spikinessPower(float:1.0) Relative thickness of light bands (power, >1 to make dark bands sparser)#Param: maxUpward(float:0) Instant temperature increase limit, as required by some firmwares ©#Param: zOffset(float:0) Vertical shift of the variations, as shown at the end of the gcode file (mm)## Original Author: Jeremie Francois (jeremie.francois@gmail.com)# License: GNU Affero General Public License http://www.gnu.org/licenses/agpl.html# Heavily Modified by: Zhu Da Hai. Sept 2, 2016 (appologies to Jeremie)# Compatible with: Cura 2.3 Beta#from ..Script import Scriptimport reimport randomimport mathimport timeimport datetimeeol = "\n"import inspectimport sysimport getoptclass WoodGrain(Script):   def __init__(self):       super().__init__()   def getSettingDataString(self):       return {           "name":"Wood Grain",           "key": "WoodGrain",           "metadata":{},           "version": 2,           "settings":           {               "a_minTemp":                {                   "label": "Min Temp",                   "description": "Mininmum print temperature (degree C)",                   "unit": "c",                   "type": "float",                   "default_value": 180               },               "b_maxTemp":                {                   "label": "Max Temp",                   "description": "Maximun print temperature (degree C)",                   "unit": "c",                   "type": "float",                   "default_value": 230               },               "c_firstTemp":                {                   "label": "First Temp",                   "description": "Starting temperature (degree C, zero to disable)",                   "unit": "c",                   "type": "float",                   "default_value": 0               },               "d_grainSize":                {                   "label": "Grain Size",                   "description": "Average 'wood grain' size (mm)",                   "unit": "mm",                   "type": "float",                   "default_value": 3.0               },               "e_maxUpward":                {                   "label": "Max Upward",                   "description": "Instant temperature increase limit, as required by some firmwares ©",                   "unit": "c",                   "type": "float",                   "default_value": 0               },               "f_zOffset":                {                   "label": "Z-Offset",                   "description": "Vertical shift of the variations, as shown at the end of the gcode file (mm)",                   "unit": "mm",                   "type": "float",                   "default_value": 1.0               },               "g_randomSeed":                {                   "label": "Random Seed",                   "description": "Seed for Random Number Generator (0 = no seed - randomize)",                   "unit": "",                   "type": "int",                   "default_value": 0               },               "h_spikinessPower":                {                   "label": "Spikiness Power",                   "description": "Relative thickness of light bands (power, >1 to make dark bands sparser)",                   "unit": "",                   "type": "float",                   "default_value": 1.0               }           }       }   def getValue(self, line, key, default = None):       if not key in line or (';' in line and line.find(key) > line.find(';')):           return default       sub_part = line[line.find(key) + len(key):]       m = re.search('^[-+]?[0-9]+\.?[0-9]*', sub_part)       if m is None:           return default       try:           return float(m.group(0))       except:           return default   def getZ(self, line, default = None):       # new 20130727: now support G0 in addition to G1       if self.getValue(line, 'G') == 0 or self.getValue(line, 'G') == 1:           return self.getValue(line, 'Z', default)       else:           return default   try:       xrange   except NameError:       xrange = range   def perlinToNormalizedWood(self, z, zOffset, grainSize, spikinessPower, perlin):       banding = 3       octaves = 2       persistence = 0.7       noise = banding * perlin.fractal(octaves, persistence, 0,0, (z+zOffset)/(grainSize*2));       noise = (noise - math.floor(noise)) # normalized to [0,1]       noise= math.pow(noise, spikinessPower)       return noise   def noiseToTemp(self, noise, maxTemp, minTemp):       return minTemp + noise * (maxTemp - minTemp)   class Perlin:       # Perlin noise: http://mrl.nyu.edu/~perlin/noise/       def __init__(self, tiledim=256):           self.tiledim= tiledim           self.perm = [None]*2*tiledim           permutation = []           # xrange changed to range for Python3           for value in range(tiledim):               permutation.append(value)           random.shuffle(permutation)           # xrange changed to range for Python3           for i in range(tiledim):               self.perm[i] = permutation[i]               self.perm[tiledim+i] = self.perm[i]       def fade(self, t):           return t * t * t * (t * (t * 6 - 15) + 10)       def lerp(self, t, a, b):           return a + t * (b - a)       def grad(self, hash, x, y, z):           #CONVERT LO 4 BITS OF HASH CODE INTO 12 GRADIENT DIRECTIONS.           h = hash & 15           if h < 8: u = x           else:     u = y           if h < 4: v = y           else:               if h == 12 or h == 14: v = x               else:                  v = z           if h&1 == 0: first = u           else:        first = -u           if h&2 == 0: second = v           else:        second = -v           return first + second       def noise(self, x,y,z):           #FIND UNIT CUBE THAT CONTAINS POINT.           X = int(x)&(self.tiledim-1)           Y = int(y)&(self.tiledim-1)           Z = int(z)&(self.tiledim-1)           #FIND RELATIVE X,Y,Z OF POINT IN CUBE.           x -= int(x)           y -= int(y)           z -= int(z)           #COMPUTE FADE CURVES FOR EACH OF X,Y,Z.           u = self.fade(x)           v = self.fade(y)           w = self.fade(z)           #HASH COORDINATES OF THE 8 CUBE CORNERS           A = self.perm[X  ]+Y; AA = self.perm[A]+Z; AB = self.perm[A+1]+Z           B = self.perm[X+1]+Y; BA = self.perm[b]+Z; BB = self.perm[b+1]+Z           #AND ADD BLENDED RESULTS FROM 8 CORNERS OF CUBE           return self.lerp(w,self.lerp(v,                   self.lerp(u,self.grad(self.perm[AA  ],x  ,y  ,z  ), self.grad(self.perm[bA  ],x-1,y  ,z  )),                   self.lerp(u,self.grad(self.perm[AB  ],x  ,y-1,z  ), self.grad(self.perm[bB  ],x-1,y-1,z  ))),               self.lerp(v,                   self.lerp(u,self.grad(self.perm[AA+1],x  ,y  ,z-1), self.grad(self.perm[bA+1],x-1,y  ,z-1)),                   self.lerp(u,self.grad(self.perm[AB+1],x  ,y-1,z-1), self.grad(self.perm[bB+1],x-1,y-1,z-1))))       def fractal(self, octaves, persistence, x,y,z, frequency=1):           value = 0.0           amplitude = 1.0           totalAmplitude= 0.0           # xrange changed to range for Python3           for octave in range(octaves):               n= self.noise(x*frequency,y*frequency,z*frequency)               value += amplitude * n               totalAmplitude += amplitude               amplitude *= persistence               frequency *= 2           return value / totalAmplitude   def execute(self, data):       minTemp = float(self.getSettingValueByKey("a_minTemp"))       maxTemp = float(self.getSettingValueByKey("b_maxTemp"))       firstTemp = float(self.getSettingValueByKey("c_firstTemp"))       grainSize = float(self.getSettingValueByKey("d_grainSize"))       maxUpward = float(self.getSettingValueByKey("e_maxUpward"))       zOffset = float(self.getSettingValueByKey("f_zOffset"))       randomSeed = int(self.getSettingValueByKey("g_randomSeed"))       spikinessPower = float(self.getSettingValueByKey("h_spikinessPower"))       myStr = ""       if randomSeed != 0:           random.seed( randomSeed )       # new 20130727: limit the number of changes for helicoidal/Joris slicing method       minimumChangeZ=0.1       perlin = WoodGrain.Perlin()       # Generate normalized noises, and then temperatures (will be indexed by Z value)       noises = {}       # first value is hard encoded since some slicers do not write a Z0 at the first layer!       # TODO: keep only Z changes that are followed by real extrusion (ie. discard non-printing head movements!)       noises[0] = self.perlinToNormalizedWood(0, zOffset, grainSize, spikinessPower, perlin)       pendingNoise = None       formerZ = -1       for layer in data:           lines = layer.split("\n")           for line in lines:               thisZ = self.getZ(line, formerZ)               if thisZ > 2 + formerZ:                   formerZ = thisZ                   #noises = {} # some damn slicers include a big negative Z shift at the beginning, which impacts the min/max range               elif abs ( thisZ - formerZ ) > minimumChangeZ:                   formerZ = thisZ                   noises[thisZ] = self.perlinToNormalizedWood(thisZ, zOffset, grainSize, spikinessPower, perlin);       lastPatchZ = thisZ; # record when to stop patching M104, to leave the last one switch the temperature off       # normalize built noises       noisesMax = noises[max(noises, key = noises.get )]       noisesMin = noises[min(noises, key = noises.get )]       for z,v in noises.items():           noises[z]= (noises[z]-noisesMin)/(noisesMax-noisesMin)       #       # new 20130727: header and first (blocking) temperature change       #       warmingTempCommands="M230 S0" + eol # enable wait for temp on the first change       if firstTemp == 0:           warmingTempCommands+= ("M104 S%i" + eol) % self.noiseToTemp(0, maxTemp, minTemp)       else:           warmingTempCommands+= ("M104 S%i" + eol) % firstTemp       # The two following commands depends on the firmware:       warmingTempCommands+= "M230 S1" + eol # now disable wait for temp on the first change       warmingTempCommands+= "M116" + eol # wait for the temperature to reach the setting (M109 is obsolete)       # Prepare a transposed temperature graph for the end of the file       graphStr=";WoodGraph: Wood temperature graph (from "+str(minTemp)+"C to "+str(maxTemp)+"C, grain size "+str(grainSize)+"mm, z-offset "+str(zOffset)+")"       if maxUpward:           graphStr+=", temperature increases capped at "+str(maxUpward)       graphStr+=":"       graphStr+=eol       thisZ = -1       formerZ = -1       warned = 0       header = 1       savelayer = 0       postponedTempDelta=0 # only when maxUpward is used       postponedTempLast=None # only when maxUpward is used       skiplines=0       # Now Modify the gCode       for layer in data:           if header == 1:               index = data.index(layer)                layer = warmingTempCommands + layer               data[index] = layer #Override the data of this layer with the modified data               header = 0           lines = layer.split("\n")           for line in lines:               if "; set extruder " in line.lower(): # special fix for BFB                   index = data.index(layer)                    layer = layer.replace(line,line + "\n" + warmingTempCommands)                   warmingTempCommands=""                   savelayer = 1               elif skiplines > 0:                   skiplines= skiplines-1;               elif ";woodified" in line.lower():                   skiplines=4 # skip 4 more lines after our comment               elif not ";woodgraph" in line.lower(): # forget optional former temp graph lines in the file                   if thisZ == lastPatchZ:                       line = line # dummy                   elif not "m104" in line.lower(): # forget any previous temp in the file                       thisZ = self.getZ(line, formerZ)                       if thisZ != formerZ and thisZ in noises:                           if firstTemp != 0 and thisZ<=0.5: # if specifed, keep the first temp for the first 0.5mm                               temp= firstTemp                           else:                               temp= self.noiseToTemp(noises[thisZ], maxTemp, minTemp)                               # possibly cap temperature change upward                               temp += postponedTempDelta;                               #print("ppdelta=%f\n" % postponedTempDelta)                               postponedTempDelta = 0                               if postponedTempLast!= None and maxUpward > 0 and temp > postponedTempLast + maxUpward:                                   postponedTempDelta = temp - (postponedTempLast + maxUpward)                                   temp= postponedTempLast + maxUpward                               if temp > maxTemp:                                   postponedTempDelta= 0                                   temp= maxTemp                               postponedTempLast = temp                               index = data.index(layer)                                layer = layer.replace(line,line + "\n" + ("M104 S%i ; Wood Grain" + eol) % temp)                               savelayer = 1                           formerZ = thisZ                           # Build the corresponding graph line                           #t = int(19 * noises[thisZ])                           t= int(19 * (temp-minTemp)/(maxTemp-minTemp))                           myStr = ";WoodGraph: Z %03f " % thisZ                           myStr += "@%3iC | " % temp                           # xrange changed to range for Python3                           for i in range(0,t):                               myStr += "#"                           # xrange changed to range for Python3                           for i in range(t+1,20):                               myStr += "."                           graphStr += myStr + eol           if savelayer == 1:               data[index] = layer               savelayer = 0       index = data.index(layer)       layer = layer + graphStr + eol       data[index] = layer       return data    

 

  • Like 2

Share this post


Link to post
Share on other sites
Posted · Plugin for woodFill or similar filament

Wood Grain Post Processing Plugin for Cura 2.3 (Beta) is done!

 

Great, thanks!

I just imported it an checked - everythings working :-)

Share this post


Link to post
Share on other sites
Posted · Plugin for woodFill or similar filament

Hey Guys, I'm using Cura 2.4 on a mac. I dropped the new code that DaHai8 made into my Cura plugins folder and nothing. I can't get this to work. Any suggestions?

Share this post


Link to post
Share on other sites
Posted · Plugin for woodFill or similar filament

It should go in the plugins/PostProcessingPlugin/scripts folder.

Does it not show up in the Extensions -> Post Processing -> Modify G-Code window?

I'm not very familiar with macs, do you need to mark it as 'executable' like on Unix systems?

Share this post


Link to post
Share on other sites
Posted · Plugin for woodFill or similar filament

sorry but i tried to follow your instructions but i probably miss something.

Is it possible to have the .py file?

Thank you

Share this post


Link to post
Share on other sites
Posted · Plugin for woodFill or similar filament

To use DaHai8's plugin on a mac:

Step 1) Download the plugin here: http://bitman.org/dahai/WoodGrain.zip

Step 2) Unzip and place WoodGrain.py in the following dir:

/Applications/Cura.app/Contents/Resources/plugins/plugins/PostProcessingPlugin/scripts

Step 3) Re-Launch Cura and select:

Extensions --> Post Processing --> Modify G-Code

Step 4) Click "Add a Script" --> "Wood Grain"

Step 5) ¯\_(ツ)_/¯

Step 6) Profit

Share this post


Link to post
Share on other sites
Posted · Plugin for woodFill or similar filament

Hello,

I just tried to use this script and had some troubles.

I am on Windows with Cura 2.6.2 and able to use the plugin, but only the 2 first layers (layer 0 and layer 1) are affected by the temp changes after postprocessing.

After layer 1, all the rest of code stays at the same temp and I don't understand why.

I tried different wood grain paramaters, but it is still the same.

Thanks for helping

Share this post


Link to post
Share on other sites
Posted · Plugin for woodFill or similar filament

It's the +0.5 relative Z hop in the End Gcode of the printer profile that's confusing the script. It's written to only know about absolute moves.

I'm working on a fix and should have it posted soon.

Cheers

Share this post


Link to post
Share on other sites
Posted · Plugin for woodFill or similar filament

I have that bug fixed and its posted on my site for anyone to pick up

Cura Post Processing Plugins

I did uncover another bug that I haven't had time to fix: sometimes (usually on .2mm layer heights) it will not do any Wood Grain temperature mod on the last few layers, no will it save the Wood Grain Graph at the end of the gcode file.

I'll try to get those fixed soon, but I don't consider them critical at the moment.

Share this post


Link to post
Share on other sites
Posted · Plugin for woodFill or similar filament

Hey @DaHai8! I have been using your WoodGrain.py for all my wood prints and its been awesome! Thank you so much! But now with Cura 3.6.0, I can't get it to work. Although it still works with Cura 3.5.1 just fine. 

 

Do you know how I can edit the code to work with Cura 3.6.0? Or do you have an updated version you can share? Thanks again for your awesome contributions!

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.


  • Our picks

    • Ultimaker Cura 4.0 | Stable available!
      Ultimaker Cura 4.0 is mainly focused on the improved user interface and cloud integration.
      As always, we want to collect your user feedback for this release. If there are any improvements you can think of, feel free to mention it here and help us to shape the next release.
      • 23 replies
×
×
  • Create New...

Important Information

Welcome to the Ultimaker Community of 3D printing experts. Visit the following links to read more about our Terms of Use or our Privacy Policy. Thank you!