Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Linear advance tower #119

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion AutoTowersGenerator.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
from .Controllers.RetractTowerController import RetractTowerController
from .Controllers.SpeedTowerController import SpeedTowerController
from .Controllers.TempTowerController import TempTowerController
from .Controllers.LinearAdvanceTowerController import LinearAdvanceTowerController

# not sure it's necessar i18n could be store in a different place ?
Resources.addSearchPath(
Expand All @@ -49,7 +50,7 @@
class AutoTowersGenerator(QObject, Extension):

# Add additional controller classes to this list
_controllerClasses = [BedLevelPatternController, FanTowerController, FlowTowerController, RetractTowerController, SpeedTowerController, TempTowerController]
_controllerClasses = [BedLevelPatternController, FanTowerController, FlowTowerController, RetractTowerController, SpeedTowerController, TempTowerController, LinearAdvanceTowerController]



Expand Down
139 changes: 139 additions & 0 deletions Controllers/LinearAdvanceTowerController.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
# Import the correct version of PyQt
try:
from PyQt6.QtCore import QObject, pyqtSlot, pyqtSignal, pyqtProperty
except ImportError:
from PyQt5.QtCore import QObject, pyqtSlot, pyqtSignal, pyqtProperty

import os

from UM.Logger import Logger
from UM.i18n import i18nCatalog
from UM.Resources import Resources

from .ControllerBase import ControllerBase
from ..Models.LinearAdvanceTowerModel import LinearAdvanceTowerModel

# Import the script that does the actual post-processing
from ..Postprocessing import LinearAdvanceTower_PostProcessing

Resources.addSearchPath(
os.path.join(os.path.join(os.path.abspath(os.path.dirname(__file__)),'..'),'Resources')
) # Plugin translation file import
catalog = i18nCatalog("autotowers")

class LinearAdvanceTowerController(ControllerBase):

_openScadFilename = 'latower.scad'
_qmlFilename = 'LATowerDialog.qml'

_criticalPropertiesTable = {
'adaptive_layer_height_enabled': (ControllerBase.ContainerId.GLOBAL_CONTAINER_STACK, False),
'layer_height': (ControllerBase.ContainerId.GLOBAL_CONTAINER_STACK, None),
'meshfix_union_all_remove_holes': (ControllerBase.ContainerId.ACTIVE_EXTRUDER_STACK, False),
'support_enable': (ControllerBase.ContainerId.GLOBAL_CONTAINER_STACK, False),
'top_bottom_thickness': (ControllerBase.ContainerId.GLOBAL_CONTAINER_STACK, 0),
'top_thickness': (ControllerBase.ContainerId.GLOBAL_CONTAINER_STACK, 0),
'bottom_thickness': (ControllerBase.ContainerId.GLOBAL_CONTAINER_STACK, 0),
'top_layers': (ControllerBase.ContainerId.GLOBAL_CONTAINER_STACK, 0),
'bottom_layers': (ControllerBase.ContainerId.GLOBAL_CONTAINER_STACK, 0),
'wall_thickness': (ControllerBase.ContainerId.GLOBAL_CONTAINER_STACK, 0),
'wall_line_count': (ControllerBase.ContainerId.GLOBAL_CONTAINER_STACK, 2),
'infill_sparse_density': (ControllerBase.ContainerId.GLOBAL_CONTAINER_STACK, 0),
'infill_wall_line_count': (ControllerBase.ContainerId.GLOBAL_CONTAINER_STACK, 0),
'adhesion_type': (ControllerBase.ContainerId.GLOBAL_CONTAINER_STACK, "brim"),
'brim_line_count': (ControllerBase.ContainerId.GLOBAL_CONTAINER_STACK, 10),
'brim_width': (ControllerBase.ContainerId.GLOBAL_CONTAINER_STACK, 4),

}



def __init__(self, guiDir, stlDir, loadStlCallback, generateStlCallback, pluginName):
dataModel = LinearAdvanceTowerModel(stlDir=stlDir)
super().__init__(name=catalog.i18nc("@test", "Linear Advance Tower"), guiDir=guiDir, loadStlCallback=loadStlCallback, generateStlCallback=generateStlCallback, qmlFilename=self._qmlFilename, criticalPropertiesTable=self._criticalPropertiesTable, dataModel=dataModel, pluginName=pluginName)



@pyqtSlot()
def dialogAccepted(self)->None:
''' This method is called by the dialog when the "Generate" button is clicked '''

if self._dataModel.presetSelected:
# Load a preset tower
self._loadPresetPATower()
else:
# Generate a custom tower using the user's settings
self._generateCustomPATower()



# This function is called by the main script when it's time to post-process the tower model
def postProcess(self, gcode, enable_lcd_messages=False, enable_advanced_gcode_comments=True)->list:
''' This method is called to post-process the gcode before it is sent to the printer or disk '''

# Collect the post-processing data
baseHeight = 0
sectionHeight = self._dataModel.optimalSectionHeight
initialLayerHeight = self._dataModel.initialLayerHeight
layerHeight = self._dataModel.layerHeight
startKfactor = self._dataModel.startKfactor
kfactorChange = self._dataModel.kfactorChange

# Call the post-processing script
gcode = LinearAdvanceTower_PostProcessing.execute(
gcode=gcode,
base_height=baseHeight,
section_height=sectionHeight,
initial_layer_height=initialLayerHeight,
layer_height=layerHeight,
start_kfactor=startKfactor,
kfactor_change=kfactorChange,
enable_lcd_messages=enable_lcd_messages,
enable_advanced_gcode_comments = enable_advanced_gcode_comments,
enable_smooth_change=True
)

return gcode



def _loadPresetPATower(self)->None:
''' Load a preset tower '''

# Determine the path of the STL file to load
stlFilePath = self._dataModel.presetFilePath

# Determine the tower name
towerName = f'Preset {self._dataModel.presetName}'

# Use the callback to load the preset STL file
self._loadStlCallback(self, towerName, stlFilePath, self.postProcess)



def _generateCustomPATower(self)->None:
''' Generate a custom tower '''

# Collect data from the data model
openScadFilename = self._openScadFilename
startKfactor = self._dataModel.startKfactor
endKfactor = self._dataModel.endKfactor
kfactorChange = self._dataModel.kfactorChange
sectionHeight = self._dataModel.optimalSectionHeight
towerLabel = self._dataModel.towerLabel
towerDescription = self._dataModel.towerDescription

# Compile the parameters to send to OpenSCAD
openScadParameters = {}
openScadParameters ['Starting_Value'] = startKfactor
openScadParameters ['Ending_Value'] = endKfactor
openScadParameters ['Value_Change'] = kfactorChange
openScadParameters ['Section_Height'] = sectionHeight
openScadParameters ['Column_Label'] = towerLabel
openScadParameters ['Tower_Label'] = towerDescription

# Determine the tower name
towerName = f'Custom Linear Advance Tower - {startKfactor}-{endKfactor}x{kfactorChange}'

# Send the filename and parameters to the model callback
self._generateStlCallback(self, towerName, self._openScadFilename, openScadParameters, self.postProcess)
190 changes: 190 additions & 0 deletions Models/LinearAdvanceTowerModel.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
# Import the correct version of PyQt
try:
from PyQt6.QtCore import pyqtSignal, pyqtProperty
except ImportError:
from PyQt5.QtCore import pyqtSignal, pyqtProperty

import os

from UM.Logger import Logger
from UM.i18n import i18nCatalog
from UM.Resources import Resources

from .ModelBase import ModelBase

Resources.addSearchPath(
os.path.join(os.path.join(os.path.abspath(os.path.dirname(__file__)),'..'),'Resources')
) # Plugin translation file import
catalog = i18nCatalog("autotowers")

class LinearAdvanceTowerModel(ModelBase):

# The available pa tower presets
_presetsTable = [
{'name': catalog.i18nc("@model", "K-factor 0.0 - 0.2") , 'filename': 'Linear Advance Tower - K 0.0-0.2.stl', 'start K': '0.0', 'K change': '0.02'},
{'name': catalog.i18nc("@model", "K-factor - 0.0 - 2.0") , 'filename': 'Linear Advance Tower - K 0.0-2.0.stl', 'start K': '0.0', 'K change': '0.2'},
]


# Make the presets availabe to QML
presetsModelChanged = pyqtSignal()

@pyqtProperty(list, notify=presetsModelChanged)
def presetsModel(self):
return self._presetsTable



# The selected fan tower preset index
_presetIndex = 0

presetIndexChanged = pyqtSignal()

def setPresetIndex(self, value)->None:
self._presetIndex = int(value)
self.presetIndexChanged.emit()

@pyqtProperty(int, notify=presetIndexChanged, fset=setPresetIndex)
def presetIndex(self)->int:
return self._presetIndex

@pyqtProperty(bool, notify=presetIndexChanged)
def presetSelected(self)->bool:
return self._presetIndex < len(self._presetsTable)

@pyqtProperty(str, notify=presetIndexChanged)
def presetName(self)->str:
return self._presetsTable[self.presetIndex]['name']

@pyqtProperty(str, notify=presetIndexChanged)
def presetFileName(self)->str:
return self._presetsTable[self.presetIndex]['filename']

@pyqtProperty(str, notify=presetIndexChanged)
def presetFilePath(self)->str:
return self._buildStlFilePath(self.presetFileName)

@pyqtProperty(str, notify=presetIndexChanged)
def presetStartKfactorStr(self)->str:
return self._presetsTable[self.presetIndex]['start K']

@pyqtProperty(float, notify=presetIndexChanged)
def presetStartKfactor(self)->float:
return float(self.presetStartKfactorStr)

@pyqtProperty(str, notify=presetIndexChanged)
def presetKfactorChangeStr(self)->str:
return self._presetsTable[self.presetIndex]['K change']

@pyqtProperty(float, notify=presetIndexChanged)
def presetKfactorChange(self)->float:
return float(self.presetKfactorChangeStr)



# The icon to display on the dialog
dialogIconChanged = pyqtSignal()

@pyqtProperty(str, notify=dialogIconChanged)
def dialogIcon(self)->str:
return 'latower_icon.png'



# The starting K-factor value for the tower
_startKfactorStr = '0.0'

startKfactorStrChanged = pyqtSignal()

def setStartKfactorStr(self, value)->None:
self._startKfactorStr = value
self.startKfactorStrChanged.emit()

@pyqtProperty(str, notify=startKfactorStrChanged, fset=setStartKfactorStr)
def startKfactorStr(self)->str:
# Allow the preset to override this setting
if self.presetSelected:
return self.presetStartKfactorStr
else:
return self._startKfactorStr

@pyqtProperty(float, notify=startKfactorStrChanged)
def startKfactor(self)->float:
return float(self.startKfactorStr)



# The ending K-factor value for the tower
_endKfactorStr = '0.2'

endKfactorStrChanged = pyqtSignal()

def setEndKfactorStr(self, value)->None:
self._endKfactorStr = value
self.endKfactorStrChanged.emit()

@pyqtProperty(str, notify=endKfactorStrChanged, fset=setEndKfactorStr)
def endKfactorStr(self)->str:
return self._endKfactorStr

@pyqtProperty(float, notify=endKfactorStrChanged)
def endKfactor(self)->float:
return float(self.endKfactorStr)



# The amount to change the K-factor between tower sections
_kfactorChangeStr = '0.02'

kfactorChangeStrChanged = pyqtSignal()

def setKfactorChangeStr(self, value)->None:
self._kfactorChangeStr = value
self.kfactorChangeStrChanged.emit()

@pyqtProperty(str, notify=kfactorChangeStrChanged, fset=setKfactorChangeStr)
def kfactorChangeStr(self)->str:
# Allow the preset to override this setting
if self.presetSelected:
return self.presetKfactorChangeStr
else:
return self._kfactorChangeStr

@pyqtProperty(float, notify=kfactorChangeStrChanged)
def kfactorChange(self)->float:
return float(self.kfactorChangeStr)



# The label to add to the tower
_towerLabel = ''

towerLabelChanged = pyqtSignal()

def setTowerLabel(self, value)->None:
self._towerLabel = value
self.towerLabelChanged.emit()

@pyqtProperty(str, notify=towerLabelChanged, fset=setTowerLabel)
def towerLabel(self)->str:
return self._towerLabel



# The description to carve up the side of the tower
_towerDescription = 'K-factor'

towerDescriptionChanged = pyqtSignal()

def setTowerDescription(self, value)->None:
self._towerDescription = value
self.towerDescriptionChanged.emit()

@pyqtProperty(str, notify=towerDescriptionChanged, fset=setTowerDescription)
def towerDescription(self)->str:
return self._towerDescription



def __init__(self, stlDir):
super().__init__(stlDir=stlDir)
2 changes: 1 addition & 1 deletion Postprocessing/FanTower_PostProcessing.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ def execute(gcode, base_height:float, section_height:float, initial_layer_height
after_bridge = False

# Iterate over each line in the g-code
for line_index, line, lines, start_of_new_section in Common.LayerEnumerate(gcode, base_height, section_height, initial_layer_height, layer_height, enable_advanced_gcode_comments):
for line_index, line, lines, start_of_new_section, _, _ in Common.LayerEnumerate(gcode, base_height, section_height, initial_layer_height, layer_height, enable_advanced_gcode_comments):

# Handle each new tower section
if start_of_new_section:
Expand Down
2 changes: 1 addition & 1 deletion Postprocessing/FlowTower_PostProcessing.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ def execute(gcode, base_height:float, section_height:float, initial_layer_height
updated_extrusion_position = None

# Iterate over each line in the g-code
for line_index, line, lines, start_of_new_section in Common.LayerEnumerate(gcode, base_height, section_height, initial_layer_height, layer_height, enable_advanced_gcode_comments):
for line_index, line, lines, start_of_new_section, _, _ in Common.LayerEnumerate(gcode, base_height, section_height, initial_layer_height, layer_height, enable_advanced_gcode_comments):

# Handle each new tower section
if start_of_new_section:
Expand Down
Loading