mirror of
https://git.mirrors.martin98.com/https://github.com/Ultimaker/Cura
synced 2025-08-06 00:16:18 +08:00
Merge branch 'master' of github.com:daid/Cura
Conflicts: Cura/gui/sliceProgessPanel.py
This commit is contained in:
commit
1b6cb8913c
@ -824,7 +824,7 @@ class TriangleMesh( group.Group ):
|
||||
halfHeight = 0.5 * self.layerHeight
|
||||
self.zoneArrangement = ZoneArrangement(self.layerHeight, self.getTransformedVertexes())
|
||||
layerTop = self.cornerMaximum.z - halfHeight * 0.5
|
||||
z = self.cornerMinimum.z + halfHeight
|
||||
z = halfHeight
|
||||
layerCount = int((layerTop - z) / self.layerHeight) + 1
|
||||
while z < layerTop:
|
||||
getLoopLayerAppend(self.loopLayers, layerCount, z).loops = self.getLoopsFromMesh(self.zoneArrangement.getEmptyZ(z))
|
||||
|
@ -113,6 +113,9 @@ def getProfileInformation():
|
||||
'SwapYZ': storedSetting("swap_yz"),
|
||||
'Scale': storedSettingFloat("model_scale"),
|
||||
'Rotate': storedSettingFloat("model_rotate_base"),
|
||||
'CenterX': storedSettingFloat("machine_center_x"),
|
||||
'CenterY': storedSettingFloat("machine_center_y"),
|
||||
'AlternativeCenterFile': storedSetting("alternative_center"),
|
||||
},'scale': {
|
||||
'Activate_Scale': "False",
|
||||
'XY_Plane_Scale_ratio': DEFSET,
|
||||
@ -171,7 +174,7 @@ def getProfileInformation():
|
||||
'Surrounding_Angle_degrees': DEFSET,
|
||||
'Thread_Sequence_Choice': storedSetting('sequence'),
|
||||
},'multiply': {
|
||||
'Activate_Multiply': "True",
|
||||
'Activate_Multiply': "False",
|
||||
'Center_X_mm': storedSettingFloat("machine_center_x"),
|
||||
'Center_Y_mm': storedSettingFloat("machine_center_y"),
|
||||
'Number_of_Columns_integer': storedSetting('model_multiply_x'),
|
||||
|
@ -185,8 +185,11 @@ class CarveRepository:
|
||||
self.flipZ = settings.BooleanSetting().getFromValue('FlipZ', self, False)
|
||||
self.swapXZ = settings.BooleanSetting().getFromValue('SwapXZ', self, False)
|
||||
self.swapYZ = settings.BooleanSetting().getFromValue('SwapYZ', self, False)
|
||||
self.centerX = settings.FloatSpin().getFromValue(0.0, 'CenterX', self, 1000.0, 0.0)
|
||||
self.centerY = settings.FloatSpin().getFromValue(0.0, 'CenterY', self, 1000.0, 0.0)
|
||||
self.scale = settings.FloatSpin().getFromValue( 0.1, 'Scale', self, 10.0, 1.0 )
|
||||
self.rotate = settings.FloatSpin().getFromValue( -180.0, 'Rotate', self, 180.0, 0.0 )
|
||||
self.alternativeCenter = settings.StringSetting().getFromValue('AlternativeCenterFile', self, '')
|
||||
|
||||
|
||||
def execute(self):
|
||||
@ -219,16 +222,6 @@ class CarveSkein:
|
||||
mat10 = math.sin(rotate) * scaleX
|
||||
mat11 = math.cos(rotate) * scaleY
|
||||
|
||||
minZ = carving.getMinimumZ()
|
||||
minSize = carving.getCarveCornerMinimum()
|
||||
maxSize = carving.getCarveCornerMaximum()
|
||||
for v in carving.vertexes:
|
||||
v.z -= minZ
|
||||
v.x -= minSize.x + (maxSize.x - minSize.x) / 2
|
||||
v.y -= minSize.y + (maxSize.y - minSize.y) / 2
|
||||
#v.x += self.machineCenter.x
|
||||
#v.y += self.machineCenter.y
|
||||
|
||||
for i in xrange(0, len(carving.vertexes)):
|
||||
x = carving.vertexes[i].x
|
||||
y = carving.vertexes[i].y
|
||||
@ -242,6 +235,34 @@ class CarveSkein:
|
||||
x * mat10 + y * mat11,
|
||||
z * scaleZ)
|
||||
|
||||
if repository.alternativeCenter.value != '':
|
||||
carving2 = svg_writer.getCarving(repository.alternativeCenter.value)
|
||||
for i in xrange(0, len(carving2.vertexes)):
|
||||
x = carving2.vertexes[i].x
|
||||
y = carving2.vertexes[i].y
|
||||
z = carving2.vertexes[i].z
|
||||
if swapXZ:
|
||||
x, z = z, x
|
||||
if swapYZ:
|
||||
y, z = z, y
|
||||
carving2.vertexes[i] = Vector3(
|
||||
x * mat00 + y * mat01,
|
||||
x * mat10 + y * mat11,
|
||||
z * scaleZ)
|
||||
minZ = carving2.getMinimumZ()
|
||||
minSize = carving2.getCarveCornerMinimum()
|
||||
maxSize = carving2.getCarveCornerMaximum()
|
||||
else:
|
||||
minZ = carving.getMinimumZ()
|
||||
minSize = carving.getCarveCornerMinimum()
|
||||
maxSize = carving.getCarveCornerMaximum()
|
||||
for v in carving.vertexes:
|
||||
v.z -= minZ
|
||||
v.x -= minSize.x + (maxSize.x - minSize.x) / 2
|
||||
v.y -= minSize.y + (maxSize.y - minSize.y) / 2
|
||||
v.x += repository.centerX.value
|
||||
v.y += repository.centerY.value
|
||||
|
||||
layerHeight = repository.layerHeight.value
|
||||
edgeWidth = repository.edgeWidth.value
|
||||
carving.setCarveLayerHeight(layerHeight)
|
||||
|
@ -223,6 +223,8 @@ class CoolSkein:
|
||||
|
||||
def addCoolTemperature(self, remainingOrbitTime):
|
||||
'Parse a gcode line and add it to the cool skein.'
|
||||
if self.repository.minimumLayerTime.value < 0.0001:
|
||||
return
|
||||
layerCool = self.repository.maximumCool.value * remainingOrbitTime / self.repository.minimumLayerTime.value
|
||||
if self.isBridgeLayer:
|
||||
layerCool = max(self.repository.bridgeCool.value, layerCool)
|
||||
@ -404,9 +406,10 @@ class CoolSkein:
|
||||
def setMultiplier(self, remainingOrbitTime):
|
||||
'Set the feed and flow rate multiplier.'
|
||||
layerTimeActive = self.getLayerTimeActive()
|
||||
self.multiplier = min(1.0, layerTimeActive / (remainingOrbitTime + layerTimeActive))
|
||||
|
||||
|
||||
if remainingOrbitTime + layerTimeActive > 0.00001:
|
||||
self.multiplier = min(1.0, layerTimeActive / (remainingOrbitTime + layerTimeActive))
|
||||
else:
|
||||
self.multiplier = 1.0
|
||||
|
||||
def main():
|
||||
'Display the cool dialog.'
|
||||
|
@ -171,6 +171,8 @@ class SkirtSkein:
|
||||
|
||||
def addSkirt(self, z):
|
||||
'At skirt at z to gcode output.'
|
||||
if len(self.outsetLoops) < 1 or len(self.outsetLoops[0]) < 1:
|
||||
return
|
||||
self.setSkirtFeedFlowTemperature()
|
||||
self.distanceFeedRate.addLine('(<skirt>)')
|
||||
oldTemperature = self.oldTemperatureInput
|
||||
|
@ -1,6 +1,7 @@
|
||||
import wx
|
||||
import sys,math,threading,os
|
||||
|
||||
from gui import gcodeTextArea
|
||||
from util import profile
|
||||
|
||||
class alterationPanel(wx.Panel):
|
||||
@ -10,8 +11,9 @@ class alterationPanel(wx.Panel):
|
||||
self.alterationFileList = ['start.gcode', 'end.gcode', 'support_start.gcode', 'support_end.gcode', 'nextobject.gcode', 'replace.csv']
|
||||
self.currentFile = None
|
||||
|
||||
self.textArea = wx.TextCtrl(self, style=wx.TE_MULTILINE|wx.TE_DONTWRAP|wx.TE_PROCESS_TAB)
|
||||
self.textArea.SetFont(wx.Font(wx.SystemSettings.GetFont(wx.SYS_ANSI_VAR_FONT).GetPointSize(), wx.FONTFAMILY_MODERN, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL))
|
||||
#self.textArea = wx.TextCtrl(self, style=wx.TE_MULTILINE|wx.TE_DONTWRAP|wx.TE_PROCESS_TAB)
|
||||
#self.textArea.SetFont(wx.Font(wx.SystemSettings.GetFont(wx.SYS_ANSI_VAR_FONT).GetPointSize(), wx.FONTFAMILY_MODERN, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL))
|
||||
self.textArea = gcodeTextArea.GcodeTextArea(self)
|
||||
self.list = wx.ListBox(self, choices=self.alterationFileList, style=wx.LB_SINGLE)
|
||||
self.list.SetSelection(0)
|
||||
self.Bind(wx.EVT_LISTBOX, self.OnSelect, self.list)
|
||||
|
103
Cura/gui/gcodeTextArea.py
Normal file
103
Cura/gui/gcodeTextArea.py
Normal file
@ -0,0 +1,103 @@
|
||||
import wx, wx.stc
|
||||
import sys,math,os
|
||||
|
||||
from util import profile
|
||||
|
||||
class GcodeTextArea(wx.stc.StyledTextCtrl):
|
||||
def __init__(self, parent):
|
||||
super(GcodeTextArea, self).__init__(parent)
|
||||
|
||||
self.SetLexer(wx.stc.STC_LEX_CONTAINER)
|
||||
self.Bind(wx.stc.EVT_STC_STYLENEEDED, self.OnStyle)
|
||||
|
||||
fontSize = wx.SystemSettings.GetFont(wx.SYS_ANSI_VAR_FONT).GetPointSize()
|
||||
fontName = wx.Font(wx.SystemSettings.GetFont(wx.SYS_ANSI_VAR_FONT).GetPointSize(), wx.FONTFAMILY_MODERN, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL).GetFaceName()
|
||||
self.SetStyleBits(5)
|
||||
self.StyleSetSpec(0, "face:%s,size:%d" % (fontName, fontSize))
|
||||
self.StyleSetSpec(1, "fore:#006000,face:%s,size:%d" % (fontName, fontSize))
|
||||
self.IndicatorSetStyle(0, wx.stc.STC_INDIC_TT)
|
||||
self.IndicatorSetForeground(0, "#0000FF")
|
||||
self.IndicatorSetStyle(1, wx.stc.STC_INDIC_SQUIGGLE)
|
||||
self.IndicatorSetForeground(1, "#FF0000")
|
||||
self.SetWrapMode(wx.stc.STC_WRAP_NONE)
|
||||
self.SetScrollWidth(1000)
|
||||
|
||||
#GCodes and MCodes as supported by Marlin
|
||||
#GCode 21 is not really supported by Marlin, but we still do not report it as error as it's often used.
|
||||
self.supportedGCodes = [0,1,2,3,4,21,28,90,91,92]
|
||||
self.supportedMCodes = [17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,42,80,81,82,83,84,85,92,104,105,106,107,109,114,115,117,119,140,190,201,202,203,204,205,206,220,221,240,301,302,303,400,500,501,502,503,999]
|
||||
|
||||
def OnStyle(self, e):
|
||||
lineNr = self.LineFromPosition(self.GetEndStyled())
|
||||
while self.PositionFromLine(lineNr) > -1:
|
||||
line = self.GetLine(lineNr)
|
||||
start = self.PositionFromLine(lineNr)
|
||||
length = self.LineLength(lineNr)
|
||||
self.StartStyling(start, 255)
|
||||
self.SetStyling(length, 0)
|
||||
if ';' in line:
|
||||
pos = line.index(';')
|
||||
self.StartStyling(start + pos, 31)
|
||||
self.SetStyling(length - pos, 1)
|
||||
length = pos
|
||||
|
||||
pos = 0
|
||||
while pos < length:
|
||||
if line[pos] in " \t\n\r":
|
||||
while pos < length and line[pos] in " \t\n\r":
|
||||
pos += 1
|
||||
else:
|
||||
end = pos
|
||||
while end < length and not line[end] in " \t\n\r":
|
||||
end += 1
|
||||
if self.checkGCodePart(line[pos:end], start + pos):
|
||||
self.StartStyling(start + pos, 0x20)
|
||||
self.SetStyling(end - pos, 0x20)
|
||||
pos = end
|
||||
lineNr += 1
|
||||
|
||||
def checkGCodePart(self, part, pos):
|
||||
if len(part) < 2:
|
||||
self.StartStyling(pos, 0x40)
|
||||
self.SetStyling(1, 0x40)
|
||||
return True
|
||||
if not part[0] in "GMXYZFESTBPIDCJ":
|
||||
self.StartStyling(pos, 0x40)
|
||||
self.SetStyling(1, 0x40)
|
||||
return True
|
||||
if part[1] == '{':
|
||||
if part[-1] != '}':
|
||||
return True
|
||||
tag = part[2:-1]
|
||||
if not profile.isProfileSetting(tag) and not profile.isPreference(tag):
|
||||
self.StartStyling(pos + 2, 0x40)
|
||||
self.SetStyling(len(tag), 0x40)
|
||||
return True
|
||||
elif part[0] in "GM":
|
||||
try:
|
||||
code = int(part[1:])
|
||||
except (ValueError):
|
||||
self.StartStyling(pos + 1, 0x40)
|
||||
self.SetStyling(len(part) - 1, 0x40)
|
||||
return True
|
||||
if part[0] == 'G':
|
||||
if not code in self.supportedGCodes:
|
||||
return True
|
||||
if part[0] == 'M':
|
||||
if not code in self.supportedMCodes:
|
||||
return True
|
||||
else:
|
||||
try:
|
||||
float(part[1:])
|
||||
except (ValueError):
|
||||
self.StartStyling(pos + 1, 0x40)
|
||||
self.SetStyling(len(part) - 1, 0x40)
|
||||
return True
|
||||
return False
|
||||
|
||||
def GetValue(self):
|
||||
return self.GetText()
|
||||
|
||||
def SetValue(self, s):
|
||||
self.SetText(s)
|
||||
|
@ -86,6 +86,7 @@ class mainWindow(configBase.configWindowBase):
|
||||
|
||||
if profile.getPreference('lastFile') != '':
|
||||
self.filelist = profile.getPreference('lastFile').split(';')
|
||||
self.SetTitle(self.filelist[-1] + ' - Cura - ' + version.getVersion())
|
||||
else:
|
||||
self.filelist = []
|
||||
self.progressPanelList = []
|
||||
@ -299,6 +300,7 @@ class mainWindow(configBase.configWindowBase):
|
||||
filelist.append(self._showOpenDialog("Open file to print"))
|
||||
if filelist[-1] == False:
|
||||
return
|
||||
self.SetTitle(filelist[-1] + ' - Cura - ' + version.getVersion())
|
||||
self.filelist = filelist
|
||||
profile.putPreference('lastFile', ';'.join(self.filelist))
|
||||
self.preview3d.loadModelFiles(self.filelist)
|
||||
|
@ -17,7 +17,9 @@ def InitGL(window, view3D, zoom):
|
||||
glViewport(0,0, size.GetWidth(), size.GetHeight())
|
||||
|
||||
glLightfv(GL_LIGHT0, GL_POSITION, [1.0, 1.0, 1.0, 0.0])
|
||||
glLightfv(GL_LIGHT1, GL_POSITION, [1.0, 1.0, 1.0, 0.0])
|
||||
|
||||
glEnable(GL_NORMALIZE)
|
||||
glEnable(GL_LIGHTING)
|
||||
glEnable(GL_LIGHT0)
|
||||
glEnable(GL_DEPTH_TEST)
|
||||
@ -198,6 +200,7 @@ def DrawBox(vMin, vMax):
|
||||
glEnd()
|
||||
|
||||
def DrawSTL(mesh):
|
||||
glEnable(GL_CULL_FACE)
|
||||
for face in mesh.faces:
|
||||
glBegin(GL_TRIANGLES)
|
||||
v1 = face.v[0]
|
||||
|
@ -7,6 +7,7 @@ import ConfigParser
|
||||
from gui import configBase
|
||||
from gui import validators
|
||||
from gui import machineCom
|
||||
from util import profile
|
||||
|
||||
class preferencesDialog(configBase.configWindowBase):
|
||||
def __init__(self, parent):
|
||||
@ -14,6 +15,8 @@ class preferencesDialog(configBase.configWindowBase):
|
||||
|
||||
wx.EVT_CLOSE(self, self.OnClose)
|
||||
|
||||
self.oldExtruderAmount = int(profile.getPreference('extruder_amount'))
|
||||
|
||||
left, right, main = self.CreateConfigPanel(self)
|
||||
configBase.TitleRow(left, 'Machine settings')
|
||||
c = configBase.SettingRow(left, 'Steps per E', 'steps_per_e', '0', 'Amount of steps per mm filament extrusion', type = 'preference')
|
||||
@ -25,6 +28,13 @@ class preferencesDialog(configBase.configWindowBase):
|
||||
c = configBase.SettingRow(left, 'Machine height (mm)', 'machine_height', '200', 'Size of the machine in mm', type = 'preference')
|
||||
validators.validFloat(c, 10.0)
|
||||
c = configBase.SettingRow(left, 'Extruder count', 'extruder_amount', ['1', '2', '3', '4'], 'Amount of extruders in your machine.', type = 'preference')
|
||||
|
||||
for i in xrange(1, self.oldExtruderAmount):
|
||||
configBase.TitleRow(left, 'Extruder %d' % (i+1))
|
||||
c = configBase.SettingRow(left, 'Offset X', 'extruder_offset_x%d' % (i), '0.0', 'The offset of your secondary extruder compared to the primary.', type = 'preference')
|
||||
validators.validFloat(c)
|
||||
c = configBase.SettingRow(left, 'Offset Y', 'extruder_offset_y%d' % (i), '0.0', 'The offset of your secondary extruder compared to the primary.', type = 'preference')
|
||||
validators.validFloat(c)
|
||||
|
||||
configBase.TitleRow(left, 'Filament settings')
|
||||
c = configBase.SettingRow(left, 'Filament density (kg/m3)', 'filament_density', '1300', 'Weight of the filament per m3. Around 1300 for PLA. And around 1040 for ABS. This value is used to estimate the weight if the filament used for the print.', type = 'preference')
|
||||
@ -42,10 +52,16 @@ class preferencesDialog(configBase.configWindowBase):
|
||||
#c = configBase.SettingRow(left, 'Slicer selection', 'slicer', ['Cura (Skeinforge based)', 'Slic3r'], 'Which slicer to use to slice objects. Usually the Cura engine produces the best results. But Slic3r is developing fast and is faster with slicing.', type = 'preference')
|
||||
c = configBase.SettingRow(left, 'Save profile on slice', 'save_profile', False, 'When slicing save the profile as [stl_file]_profile.ini next to the model.', type = 'preference')
|
||||
|
||||
self.okButton = wx.Button(left, -1, 'Ok')
|
||||
left.GetSizer().Add(self.okButton, (left.GetSizer().GetRows(), 1))
|
||||
self.okButton.Bind(wx.EVT_BUTTON, self.OnClose)
|
||||
|
||||
self.MakeModal(True)
|
||||
main.Fit()
|
||||
self.Fit()
|
||||
|
||||
def OnClose(self, e):
|
||||
if self.oldExtruderAmount != int(profile.getPreference('extruder_amount')):
|
||||
wx.MessageBox('After changing the amount of extruders you need to restart Cura for full effect.', 'Extruder amount warning.', wx.OK | wx.ICON_INFORMATION)
|
||||
self.MakeModal(False)
|
||||
self.Destroy()
|
||||
|
@ -1,11 +1,6 @@
|
||||
from __future__ import division
|
||||
|
||||
import sys
|
||||
import math
|
||||
import threading
|
||||
import re
|
||||
import time
|
||||
import os
|
||||
import sys, math, threading, re, time, os
|
||||
|
||||
from wx import glcanvas
|
||||
import wx
|
||||
@ -43,11 +38,12 @@ class previewPanel(wx.Panel):
|
||||
|
||||
self.glCanvas = PreviewGLCanvas(self)
|
||||
self.objectList = []
|
||||
self.errorList = []
|
||||
self.gcode = None
|
||||
self.objectsMinV = None
|
||||
self.objectsMaxV = None
|
||||
self.loadThread = None
|
||||
self.machineSize = util3d.Vector3(float(profile.getPreference('machine_width')), float(profile.getPreference('machine_depth')), float(profile.getPreference('machine_height')))
|
||||
self.machineSize = util3d.Vector3(profile.getPreferenceFloat('machine_width'), profile.getPreferenceFloat('machine_depth'), profile.getPreferenceFloat('machine_height'))
|
||||
self.machineCenter = util3d.Vector3(float(profile.getProfileSetting('machine_center_x')), float(profile.getProfileSetting('machine_center_y')), 0)
|
||||
|
||||
self.toolbar = toolbarUtil.Toolbar(self)
|
||||
@ -63,7 +59,6 @@ class previewPanel(wx.Panel):
|
||||
self.xrayViewButton = toolbarUtil.RadioButton(self.toolbar, group, 'view-xray-on.png', 'view-xray-off.png', 'X-Ray view', callback=self.OnViewChange)
|
||||
self.gcodeViewButton = toolbarUtil.RadioButton(self.toolbar, group, 'view-gcode-on.png', 'view-gcode-off.png', 'GCode view', callback=self.OnViewChange)
|
||||
self.mixedViewButton = toolbarUtil.RadioButton(self.toolbar, group, 'view-mixed-on.png', 'view-mixed-off.png', 'Mixed model/GCode view', callback=self.OnViewChange)
|
||||
self.OnViewChange()
|
||||
self.toolbar.AddSeparator()
|
||||
|
||||
self.layerSpin = wx.SpinCtrl(self.toolbar, -1, '', size=(21*4,21), style=wx.SP_ARROW_KEYS)
|
||||
@ -93,22 +88,21 @@ class previewPanel(wx.Panel):
|
||||
self.toolbar2.AddSeparator()
|
||||
|
||||
# Multiply
|
||||
self.mulXadd = toolbarUtil.NormalButton(self.toolbar2, self.OnMulXAddClick, 'object-mul-x-add.png', 'Increase number of models on X axis')
|
||||
self.mulXsub = toolbarUtil.NormalButton(self.toolbar2, self.OnMulXSubClick, 'object-mul-x-sub.png', 'Decrease number of models on X axis')
|
||||
self.mulYadd = toolbarUtil.NormalButton(self.toolbar2, self.OnMulYAddClick, 'object-mul-y-add.png', 'Increase number of models on Y axis')
|
||||
self.mulYsub = toolbarUtil.NormalButton(self.toolbar2, self.OnMulYSubClick, 'object-mul-y-sub.png', 'Decrease number of models on Y axis')
|
||||
|
||||
self.toolbar2.AddSeparator()
|
||||
#self.mulXadd = toolbarUtil.NormalButton(self.toolbar2, self.OnMulXAddClick, 'object-mul-x-add.png', 'Increase number of models on X axis')
|
||||
#self.mulXsub = toolbarUtil.NormalButton(self.toolbar2, self.OnMulXSubClick, 'object-mul-x-sub.png', 'Decrease number of models on X axis')
|
||||
#self.mulYadd = toolbarUtil.NormalButton(self.toolbar2, self.OnMulYAddClick, 'object-mul-y-add.png', 'Increase number of models on Y axis')
|
||||
#self.mulYsub = toolbarUtil.NormalButton(self.toolbar2, self.OnMulYSubClick, 'object-mul-y-sub.png', 'Decrease number of models on Y axis')
|
||||
#self.toolbar2.AddSeparator()
|
||||
|
||||
# Rotate
|
||||
self.rotateReset = toolbarUtil.NormalButton(self.toolbar2, self.OnRotateReset, 'object-rotate.png', 'Reset model rotation')
|
||||
self.rotate = wx.SpinCtrl(self.toolbar2, -1, profile.getProfileSetting('model_rotate_base'), size=(21*3,21), style=wx.SP_WRAP|wx.SP_ARROW_KEYS)
|
||||
self.rotate.SetRange(0, 360)
|
||||
self.Bind(wx.EVT_TEXT, self.OnRotate)
|
||||
self.rotate.Bind(wx.EVT_TEXT, self.OnRotate)
|
||||
self.toolbar2.AddControl(self.rotate)
|
||||
|
||||
self.toolbar2.Realize()
|
||||
self.updateToolbar()
|
||||
self.OnViewChange()
|
||||
|
||||
sizer = wx.BoxSizer(wx.VERTICAL)
|
||||
sizer.Add(self.toolbar, 0, flag=wx.EXPAND|wx.TOP|wx.LEFT|wx.RIGHT, border=1)
|
||||
@ -181,7 +175,6 @@ class previewPanel(wx.Panel):
|
||||
self.glCanvas.Refresh()
|
||||
|
||||
def OnLayerNrChange(self, e):
|
||||
self.gcodeDirty = True
|
||||
self.glCanvas.Refresh()
|
||||
|
||||
def updateCenterX(self):
|
||||
@ -203,8 +196,9 @@ class previewPanel(wx.Panel):
|
||||
def loadModelFiles(self, filelist):
|
||||
while len(filelist) > len(self.objectList):
|
||||
self.objectList.append(previewObject())
|
||||
for idx in xrange(len(self.objectList), len(filelist)):
|
||||
for idx in xrange(len(filelist), len(self.objectList)):
|
||||
self.objectList[idx].mesh = None
|
||||
self.objectList[idx].filename = None
|
||||
for idx in xrange(0, len(filelist)):
|
||||
obj = self.objectList[idx]
|
||||
if obj.filename != filelist[idx]:
|
||||
@ -232,12 +226,11 @@ class previewPanel(wx.Panel):
|
||||
|
||||
def doFileLoadThread(self):
|
||||
for obj in self.objectList:
|
||||
if os.path.isfile(obj.filename) and obj.fileTime != os.stat(obj.filename).st_mtime:
|
||||
if obj.filename != None and os.path.isfile(obj.filename) and obj.fileTime != os.stat(obj.filename).st_mtime:
|
||||
obj.ileTime = os.stat(obj.filename).st_mtime
|
||||
mesh = stl.stlModel()
|
||||
mesh.load(obj.filename)
|
||||
obj.dirty = False
|
||||
obj.errorList = []
|
||||
obj.mesh = mesh
|
||||
self.updateModelTransform()
|
||||
wx.CallAfter(self.updateToolbar)
|
||||
@ -272,9 +265,11 @@ class previewPanel(wx.Panel):
|
||||
pass
|
||||
|
||||
def updateToolbar(self):
|
||||
self.layerSpin.Show(self.gcode != None)
|
||||
self.gcodeViewButton.Show(self.gcode != None)
|
||||
self.mixedViewButton.Show(self.gcode != None)
|
||||
self.layerSpin.Show(self.glCanvas.viewMode == "GCode" or self.glCanvas.viewMode == "Mixed")
|
||||
if self.gcode != None:
|
||||
self.layerSpin.SetRange(1, len(self.gcode.layerList))
|
||||
self.layerSpin.SetRange(1, len(self.gcode.layerList) - 1)
|
||||
self.toolbar.Realize()
|
||||
|
||||
def OnViewChange(self):
|
||||
@ -288,6 +283,7 @@ class previewPanel(wx.Panel):
|
||||
self.glCanvas.viewMode = "GCode"
|
||||
elif self.mixedViewButton.GetValue():
|
||||
self.glCanvas.viewMode = "Mixed"
|
||||
self.updateToolbar()
|
||||
self.glCanvas.Refresh()
|
||||
|
||||
def updateModelTransform(self, f=0):
|
||||
@ -376,6 +372,7 @@ class PreviewGLCanvas(glcanvas.GLCanvas):
|
||||
self.offsetY = 0
|
||||
self.view3D = True
|
||||
self.gcodeDisplayList = None
|
||||
self.gcodeDisplayListCount = 0
|
||||
self.objColor = [[1.0, 0.8, 0.6, 1.0], [0.2, 1.0, 0.1, 1.0], [1.0, 0.2, 0.1, 1.0], [0.1, 0.2, 1.0, 1.0]]
|
||||
|
||||
def OnMouseMotion(self,e):
|
||||
@ -424,8 +421,12 @@ class PreviewGLCanvas(glcanvas.GLCanvas):
|
||||
glTranslate(0,0,-self.zoom)
|
||||
glRotate(-self.pitch, 1,0,0)
|
||||
glRotate(self.yaw, 0,0,1)
|
||||
if self.parent.objectsMaxV != None:
|
||||
glTranslate(0,0,-self.parent.objectsMaxV.z * profile.getProfileSettingFloat('model_scale') / 2)
|
||||
if self.viewMode == "GCode" or self.viewMode == "Mixed":
|
||||
if self.parent.gcode != None:
|
||||
glTranslate(0,0,-self.parent.gcode.layerList[self.parent.layerSpin.GetValue()][0].list[-1].z)
|
||||
else:
|
||||
if self.parent.objectsMaxV != None:
|
||||
glTranslate(0,0,-self.parent.objectsMaxV.z * profile.getProfileSettingFloat('model_scale') / 2)
|
||||
else:
|
||||
glScale(1.0/self.zoom, 1.0/self.zoom, 1.0)
|
||||
glTranslate(self.offsetX, self.offsetY, 0.0)
|
||||
@ -438,99 +439,113 @@ class PreviewGLCanvas(glcanvas.GLCanvas):
|
||||
machineSize = self.parent.machineSize
|
||||
opengl.DrawMachine(machineSize)
|
||||
|
||||
if self.parent.gcode != None:
|
||||
if self.gcodeDisplayList == None:
|
||||
self.gcodeDisplayList = glGenLists(1);
|
||||
if self.parent.gcodeDirty:
|
||||
self.parent.gcodeDirty = False
|
||||
glNewList(self.gcodeDisplayList, GL_COMPILE)
|
||||
prevLayerZ = 0.0
|
||||
curLayerZ = 0.0
|
||||
|
||||
layerThickness = 0.0
|
||||
filamentRadius = float(profile.getProfileSetting('filament_diameter')) / 2
|
||||
filamentArea = math.pi * filamentRadius * filamentRadius
|
||||
lineWidth = float(profile.getProfileSetting('nozzle_size')) / 2 / 10
|
||||
|
||||
curLayerNum = 0
|
||||
for layer in self.parent.gcode.layerList:
|
||||
curLayerZ = layer[0].list[1].z
|
||||
layerThickness = curLayerZ - prevLayerZ
|
||||
prevLayerZ = layer[-1].list[-1].z
|
||||
for path in layer:
|
||||
c = 1.0
|
||||
if curLayerNum != self.parent.layerSpin.GetValue():
|
||||
if curLayerNum < self.parent.layerSpin.GetValue():
|
||||
c = 0.9 - (self.parent.layerSpin.GetValue() - curLayerNum) * 0.1
|
||||
if c < 0.4:
|
||||
c = 0.4
|
||||
else:
|
||||
break
|
||||
if path.type == 'move':
|
||||
glColor3f(0,0,c)
|
||||
if path.type == 'extrude':
|
||||
if path.pathType == 'FILL':
|
||||
glColor3f(c/2,c/2,0)
|
||||
elif path.pathType == 'WALL-INNER':
|
||||
glColor3f(0,c,0)
|
||||
elif path.pathType == 'SUPPORT':
|
||||
glColor3f(0,c,c)
|
||||
elif path.pathType == 'SKIRT':
|
||||
glColor3f(0,c/2,c/2)
|
||||
else:
|
||||
glColor3f(c,0,0)
|
||||
if path.type == 'retract':
|
||||
glColor3f(0,c,c)
|
||||
if c > 0.4 and path.type == 'extrude':
|
||||
for i in xrange(0, len(path.list)-1):
|
||||
v0 = path.list[i]
|
||||
v1 = path.list[i+1]
|
||||
|
||||
# Calculate line width from ePerDistance (needs layer thickness and filament diameter)
|
||||
dist = (v0 - v1).vsize()
|
||||
if dist > 0 and layerThickness > 0:
|
||||
extrusionMMperDist = (v1.e - v0.e) / dist
|
||||
lineWidth = extrusionMMperDist * filamentArea / layerThickness / 2
|
||||
|
||||
normal = (v0 - v1).cross(util3d.Vector3(0,0,1))
|
||||
normal.normalize()
|
||||
v2 = v0 + normal * lineWidth
|
||||
v3 = v1 + normal * lineWidth
|
||||
v0 = v0 - normal * lineWidth
|
||||
v1 = v1 - normal * lineWidth
|
||||
|
||||
glBegin(GL_QUADS)
|
||||
if path.pathType == 'FILL': #Remove depth buffer fighting on infill/wall overlap
|
||||
glVertex3f(v0.x, v0.y, v0.z - 0.02)
|
||||
glVertex3f(v1.x, v1.y, v1.z - 0.02)
|
||||
glVertex3f(v3.x, v3.y, v3.z - 0.02)
|
||||
glVertex3f(v2.x, v2.y, v2.z - 0.02)
|
||||
else:
|
||||
glVertex3f(v0.x, v0.y, v0.z - 0.01)
|
||||
glVertex3f(v1.x, v1.y, v1.z - 0.01)
|
||||
glVertex3f(v3.x, v3.y, v3.z - 0.01)
|
||||
glVertex3f(v2.x, v2.y, v2.z - 0.01)
|
||||
glEnd()
|
||||
|
||||
#for v in path['list']:
|
||||
# glBegin(GL_TRIANGLE_FAN)
|
||||
# glVertex3f(v.x, v.y, v.z - 0.001)
|
||||
# for i in xrange(0, 16+1):
|
||||
# if path['pathType'] == 'FILL': #Remove depth buffer fighting on infill/wall overlap
|
||||
# glVertex3f(v.x + math.cos(math.pi*2/16*i) * lineWidth, v.y + math.sin(math.pi*2/16*i) * lineWidth, v.z - 0.02)
|
||||
# else:
|
||||
# glVertex3f(v.x + math.cos(math.pi*2/16*i) * lineWidth, v.y + math.sin(math.pi*2/16*i) * lineWidth, v.z - 0.01)
|
||||
# glEnd()
|
||||
if self.parent.gcode != None and self.parent.gcodeDirty:
|
||||
if self.gcodeDisplayListCount < len(self.parent.gcode.layerList) or self.gcodeDisplayList == None:
|
||||
if self.gcodeDisplayList != None:
|
||||
glDeleteLists(self.gcodeDisplayList, self.gcodeDisplayListCount)
|
||||
self.gcodeDisplayList = glGenLists(len(self.parent.gcode.layerList));
|
||||
self.gcodeDisplayListCount = len(self.parent.gcode.layerList)
|
||||
self.parent.gcodeDirty = False
|
||||
prevLayerZ = 0.0
|
||||
curLayerZ = 0.0
|
||||
|
||||
layerThickness = 0.0
|
||||
filamentRadius = profile.getProfileSettingFloat('filament_diameter') / 2
|
||||
filamentArea = math.pi * filamentRadius * filamentRadius
|
||||
lineWidth = profile.getProfileSettingFloat('nozzle_size') / 2 / 10
|
||||
|
||||
curLayerNum = 0
|
||||
for layer in self.parent.gcode.layerList:
|
||||
glNewList(self.gcodeDisplayList + curLayerNum, GL_COMPILE)
|
||||
glDisable(GL_CULL_FACE)
|
||||
curLayerZ = layer[0].list[1].z
|
||||
layerThickness = curLayerZ - prevLayerZ
|
||||
prevLayerZ = layer[-1].list[-1].z
|
||||
for path in layer:
|
||||
if path.type == 'move':
|
||||
glColor3f(0,0,1)
|
||||
if path.type == 'extrude':
|
||||
if path.pathType == 'FILL':
|
||||
glColor3f(0.5,0.5,0)
|
||||
elif path.pathType == 'WALL-INNER':
|
||||
glColor3f(0,1,0)
|
||||
elif path.pathType == 'SUPPORT':
|
||||
glColor3f(0,1,1)
|
||||
elif path.pathType == 'SKIRT':
|
||||
glColor3f(0,0.5,0.5)
|
||||
else:
|
||||
glBegin(GL_LINE_STRIP)
|
||||
for v in path.list:
|
||||
glVertex3f(v.x, v.y, v.z)
|
||||
glColor3f(1,0,0)
|
||||
if path.type == 'retract':
|
||||
glColor3f(0,1,1)
|
||||
if path.type == 'extrude':
|
||||
for i in xrange(0, len(path.list)-1):
|
||||
v0 = path.list[i]
|
||||
v1 = path.list[i+1]
|
||||
|
||||
# Calculate line width from ePerDistance (needs layer thickness and filament diameter)
|
||||
dist = (v0 - v1).vsize()
|
||||
if dist > 0 and layerThickness > 0:
|
||||
extrusionMMperDist = (v1.e - v0.e) / dist
|
||||
lineWidth = extrusionMMperDist * filamentArea / layerThickness / 2
|
||||
|
||||
normal = (v0 - v1).cross(util3d.Vector3(0,0,1))
|
||||
normal.normalize()
|
||||
v2 = v0 + normal * lineWidth
|
||||
v3 = v1 + normal * lineWidth
|
||||
v0 = v0 - normal * lineWidth
|
||||
v1 = v1 - normal * lineWidth
|
||||
|
||||
glBegin(GL_QUADS)
|
||||
if path.pathType == 'FILL': #Remove depth buffer fighting on infill/wall overlap
|
||||
glVertex3f(v0.x, v0.y, v0.z - 0.02)
|
||||
glVertex3f(v1.x, v1.y, v1.z - 0.02)
|
||||
glVertex3f(v3.x, v3.y, v3.z - 0.02)
|
||||
glVertex3f(v2.x, v2.y, v2.z - 0.02)
|
||||
else:
|
||||
glVertex3f(v0.x, v0.y, v0.z - 0.01)
|
||||
glVertex3f(v1.x, v1.y, v1.z - 0.01)
|
||||
glVertex3f(v3.x, v3.y, v3.z - 0.01)
|
||||
glVertex3f(v2.x, v2.y, v2.z - 0.01)
|
||||
glEnd()
|
||||
curLayerNum += 1
|
||||
|
||||
#for v in path['list']:
|
||||
# glBegin(GL_TRIANGLE_FAN)
|
||||
# glVertex3f(v.x, v.y, v.z - 0.001)
|
||||
# for i in xrange(0, 16+1):
|
||||
# if path['pathType'] == 'FILL': #Remove depth buffer fighting on infill/wall overlap
|
||||
# glVertex3f(v.x + math.cos(math.pi*2/16*i) * lineWidth, v.y + math.sin(math.pi*2/16*i) * lineWidth, v.z - 0.02)
|
||||
# else:
|
||||
# glVertex3f(v.x + math.cos(math.pi*2/16*i) * lineWidth, v.y + math.sin(math.pi*2/16*i) * lineWidth, v.z - 0.01)
|
||||
# glEnd()
|
||||
else:
|
||||
glBegin(GL_LINE_STRIP)
|
||||
for v in path.list:
|
||||
glVertex3f(v.x, v.y, v.z)
|
||||
glEnd()
|
||||
curLayerNum += 1
|
||||
glEnable(GL_CULL_FACE)
|
||||
glEndList()
|
||||
if self.viewMode == "GCode" or self.viewMode == "Mixed":
|
||||
glCallList(self.gcodeDisplayList)
|
||||
|
||||
if self.parent.gcode != None and (self.viewMode == "GCode" or self.viewMode == "Mixed"):
|
||||
glEnable(GL_COLOR_MATERIAL)
|
||||
glEnable(GL_LIGHTING)
|
||||
for i in xrange(0, self.parent.layerSpin.GetValue() + 1):
|
||||
c = 1.0
|
||||
if i < self.parent.layerSpin.GetValue():
|
||||
c = 0.9 - (self.parent.layerSpin.GetValue() - i) * 0.1
|
||||
if c < 0.4:
|
||||
c = (0.4 + c) / 2
|
||||
if c < 0.1:
|
||||
c = 0.1
|
||||
glLightfv(GL_LIGHT0, GL_DIFFUSE, [0,0,0,0])
|
||||
glLightfv(GL_LIGHT0, GL_AMBIENT, [c,c,c,c])
|
||||
glCallList(self.gcodeDisplayList + i)
|
||||
glDisable(GL_LIGHTING)
|
||||
glDisable(GL_COLOR_MATERIAL)
|
||||
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, [0.2, 0.2, 0.2, 1.0]);
|
||||
glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, [0.8, 0.8, 0.8, 1.0]);
|
||||
|
||||
glColor3f(1.0,1.0,1.0)
|
||||
glTranslate(self.parent.machineCenter.x, self.parent.machineCenter.y, 0)
|
||||
for obj in self.parent.objectList:
|
||||
if obj.mesh == None:
|
||||
@ -543,17 +558,15 @@ class PreviewGLCanvas(glcanvas.GLCanvas):
|
||||
opengl.DrawSTL(obj.mesh)
|
||||
glEndList()
|
||||
|
||||
glEnable(GL_NORMALIZE)
|
||||
if self.viewMode == "Transparent" or self.viewMode == "Mixed":
|
||||
glLightfv(GL_LIGHT0, GL_DIFFUSE, map(lambda x: x / 2, self.objColor[self.parent.objectList.index(obj)]))
|
||||
glLightfv(GL_LIGHT0, GL_AMBIENT, map(lambda x: x / 10, self.objColor[self.parent.objectList.index(obj)]))
|
||||
#If we want transparent, then first render a solid black model to remove the printer size lines.
|
||||
if self.viewMode != "Mixed":
|
||||
glDisable(GL_BLEND)
|
||||
glDisable(GL_LIGHTING)
|
||||
glColor3f(0,0,0)
|
||||
glColor3f(0.0,0.0,0.0)
|
||||
self.drawModel(obj)
|
||||
glColor3f(1,1,1)
|
||||
glColor3f(1.0,1.0,1.0)
|
||||
#After the black model is rendered, render the model again but now with lighting and no depth testing.
|
||||
glDisable(GL_DEPTH_TEST)
|
||||
glEnable(GL_LIGHTING)
|
||||
@ -608,22 +621,22 @@ class PreviewGLCanvas(glcanvas.GLCanvas):
|
||||
glEnable(GL_LIGHTING)
|
||||
self.drawModel(obj)
|
||||
|
||||
if self.viewMode == "Normal" or self.viewMode == "Transparent" or self.viewMode == "X-Ray":
|
||||
glDisable(GL_LIGHTING)
|
||||
glDisable(GL_DEPTH_TEST)
|
||||
glDisable(GL_BLEND)
|
||||
glColor3f(1,0,0)
|
||||
#glBegin(GL_LINES)
|
||||
#for err in self.parent.errorList:
|
||||
# glVertex3f(err[0].x, err[0].y, err[0].z)
|
||||
# glVertex3f(err[1].x, err[1].y, err[1].z)
|
||||
#glEnd()
|
||||
glEnable(GL_DEPTH_TEST)
|
||||
if self.viewMode == "Normal" or self.viewMode == "Transparent" or self.viewMode == "X-Ray":
|
||||
glDisable(GL_LIGHTING)
|
||||
glDisable(GL_DEPTH_TEST)
|
||||
glDisable(GL_BLEND)
|
||||
glColor3f(1,0,0)
|
||||
glBegin(GL_LINES)
|
||||
for err in self.parent.errorList:
|
||||
glVertex3f(err[0].x, err[0].y, err[0].z)
|
||||
glVertex3f(err[1].x, err[1].y, err[1].z)
|
||||
glEnd()
|
||||
glEnable(GL_DEPTH_TEST)
|
||||
glFlush()
|
||||
|
||||
def drawModel(self, obj):
|
||||
multiX = int(profile.getProfileSetting('model_multiply_x'))
|
||||
multiY = int(profile.getProfileSetting('model_multiply_y'))
|
||||
multiX = 1 #int(profile.getProfileSetting('model_multiply_x'))
|
||||
multiY = 1 #int(profile.getProfileSetting('model_multiply_y'))
|
||||
modelScale = profile.getProfileSettingFloat('model_scale')
|
||||
modelSize = (obj.mesh.getMaximum() - obj.mesh.getMinimum()) * modelScale
|
||||
glPushMatrix()
|
||||
|
@ -176,14 +176,9 @@ class printWindow(wx.Frame):
|
||||
status = ""
|
||||
if self.gcode != None:
|
||||
status += "Filament: %.2fm %.2fg\n" % (self.gcode.extrusionAmount / 1000, self.gcode.calculateWeight() * 1000)
|
||||
cost_kg = float(profile.getPreference('filament_cost_kg'))
|
||||
cost_meter = float(profile.getPreference('filament_cost_meter'))
|
||||
if cost_kg > 0.0 and cost_meter > 0.0:
|
||||
status += "Filament cost: %.2f / %.2f\n" % (self.gcode.calculateWeight() * cost_kg, self.gcode.extrusionAmount / 1000 * cost_meter)
|
||||
elif cost_kg > 0.0:
|
||||
status += "Filament cost: %.2f\n" % (self.gcode.calculateWeight() * cost_kg)
|
||||
elif cost_meter > 0.0:
|
||||
status += "Filament cost: %.2f\n" % (self.gcode.extrusionAmount / 1000 * cost_meter)
|
||||
cost = self.gcode.calculateCost()
|
||||
if cost != False:
|
||||
status += "Filament cost: %s\n" % (cost)
|
||||
status += "Print time: %02d:%02d\n" % (int(self.gcode.totalMoveTimeMinute / 60), int(self.gcode.totalMoveTimeMinute % 60))
|
||||
if self.printIdx == None:
|
||||
self.progress.SetValue(0)
|
||||
|
@ -23,10 +23,110 @@ from util import profile
|
||||
from util import util3d
|
||||
from util import stl
|
||||
from util import sliceRun
|
||||
from util import gcodeInterpreter
|
||||
|
||||
class Action():
|
||||
class Action(object):
|
||||
pass
|
||||
|
||||
class ProjectObject(stl.stlModel):
|
||||
def __init__(self, filename):
|
||||
super(ProjectObject, self).__init__()
|
||||
|
||||
self.load(filename)
|
||||
|
||||
self.filename = filename
|
||||
self.scale = 1.0
|
||||
self.rotate = 0.0
|
||||
self.flipX = False
|
||||
self.flipY = False
|
||||
self.flipZ = False
|
||||
self.swapXZ = False
|
||||
self.swapYZ = False
|
||||
self.extruder = 0
|
||||
|
||||
self.modelDisplayList = None
|
||||
self.modelDirty = False
|
||||
|
||||
self.origonalVertexes = list(self.vertexes)
|
||||
for i in xrange(0, len(self.origonalVertexes)):
|
||||
self.origonalVertexes[i] = self.origonalVertexes[i].copy()
|
||||
self.getMinimumZ()
|
||||
|
||||
self.centerX = -self.getMinimum().x + 5
|
||||
self.centerY = -self.getMinimum().y + 5
|
||||
|
||||
self.updateModelTransform()
|
||||
|
||||
self.centerX = -self.getMinimum().x + 5
|
||||
self.centerY = -self.getMinimum().y + 5
|
||||
|
||||
def updateModelTransform(self):
|
||||
rotate = self.rotate / 180.0 * math.pi
|
||||
scaleX = 1.0
|
||||
scaleY = 1.0
|
||||
scaleZ = 1.0
|
||||
if self.flipX:
|
||||
scaleX = -scaleX
|
||||
if self.flipY:
|
||||
scaleY = -scaleY
|
||||
if self.flipZ:
|
||||
scaleZ = -scaleZ
|
||||
swapXZ = self.swapXZ
|
||||
swapYZ = self.swapYZ
|
||||
mat00 = math.cos(rotate) * scaleX
|
||||
mat01 =-math.sin(rotate) * scaleY
|
||||
mat10 = math.sin(rotate) * scaleX
|
||||
mat11 = math.cos(rotate) * scaleY
|
||||
|
||||
for i in xrange(0, len(self.origonalVertexes)):
|
||||
x = self.origonalVertexes[i].x
|
||||
y = self.origonalVertexes[i].y
|
||||
z = self.origonalVertexes[i].z
|
||||
if swapXZ:
|
||||
x, z = z, x
|
||||
if swapYZ:
|
||||
y, z = z, y
|
||||
self.vertexes[i].x = x * mat00 + y * mat01
|
||||
self.vertexes[i].y = x * mat10 + y * mat11
|
||||
self.vertexes[i].z = z * scaleZ
|
||||
|
||||
for face in self.faces:
|
||||
v1 = face.v[0]
|
||||
v2 = face.v[1]
|
||||
v3 = face.v[2]
|
||||
face.normal = (v2 - v1).cross(v3 - v1)
|
||||
face.normal.normalize()
|
||||
|
||||
minZ = self.getMinimumZ()
|
||||
minV = self.getMinimum()
|
||||
maxV = self.getMaximum()
|
||||
for v in self.vertexes:
|
||||
v.z -= minZ
|
||||
v.x -= minV.x + (maxV.x - minV.x) / 2
|
||||
v.y -= minV.y + (maxV.y - minV.y) / 2
|
||||
self.getMinimumZ()
|
||||
self.modelDirty = True
|
||||
|
||||
def clone(self):
|
||||
p = ProjectObject(self.filename)
|
||||
|
||||
p.centerX = self.centerX + 5
|
||||
p.centerY = self.centerY + 5
|
||||
|
||||
p.filename = self.filename
|
||||
p.scale = self.scale
|
||||
p.rotate = self.rotate
|
||||
p.flipX = self.flipX
|
||||
p.flipY = self.flipY
|
||||
p.flipZ = self.flipZ
|
||||
p.swapXZ = self.swapXZ
|
||||
p.swapYZ = self.swapYZ
|
||||
p.extruder = self.extruder
|
||||
|
||||
p.updateModelTransform()
|
||||
|
||||
return p
|
||||
|
||||
class projectPlanner(wx.Frame):
|
||||
"Main user interface window"
|
||||
def __init__(self):
|
||||
@ -39,9 +139,15 @@ class projectPlanner(wx.Frame):
|
||||
self.list = []
|
||||
self.selection = None
|
||||
|
||||
self.machineSize = util3d.Vector3(float(profile.getPreference('machine_width')), float(profile.getPreference('machine_depth')), float(profile.getPreference('machine_height')))
|
||||
self.headSizeMin = util3d.Vector3(70,16,0)
|
||||
self.headSizeMax = util3d.Vector3(16,35,0)
|
||||
self.machineSize = util3d.Vector3(profile.getPreferenceFloat('machine_width'), profile.getPreferenceFloat('machine_depth'), profile.getPreferenceFloat('machine_height'))
|
||||
self.headSizeMin = util3d.Vector3(profile.getPreferenceFloat('extruder_head_size_min_x'), profile.getPreferenceFloat('extruder_head_size_min_y'),0)
|
||||
self.headSizeMax = util3d.Vector3(profile.getPreferenceFloat('extruder_head_size_max_x'), profile.getPreferenceFloat('extruder_head_size_max_y'),0)
|
||||
|
||||
self.extruderOffset = [
|
||||
util3d.Vector3(0,0,0),
|
||||
util3d.Vector3(profile.getPreferenceFloat('extruder_offset_x1'), profile.getPreferenceFloat('extruder_offset_y1'), 0),
|
||||
util3d.Vector3(profile.getPreferenceFloat('extruder_offset_x2'), profile.getPreferenceFloat('extruder_offset_y2'), 0),
|
||||
util3d.Vector3(profile.getPreferenceFloat('extruder_offset_x3'), profile.getPreferenceFloat('extruder_offset_y3'), 0)]
|
||||
|
||||
self.toolbar = toolbarUtil.Toolbar(self)
|
||||
|
||||
@ -55,6 +161,14 @@ class projectPlanner(wx.Frame):
|
||||
toolbarUtil.NormalButton(self.toolbar, self.OnQuit, 'exit.png', 'Close project planner')
|
||||
|
||||
self.toolbar.Realize()
|
||||
|
||||
self.toolbar2 = toolbarUtil.Toolbar(self)
|
||||
toolbarUtil.NormalButton(self.toolbar2, self.OnAddModel, 'object-add.png', 'Add model')
|
||||
toolbarUtil.NormalButton(self.toolbar2, self.OnRemModel, 'object-remove.png', 'Remove model')
|
||||
toolbarUtil.NormalButton(self.toolbar2, self.OnMoveUp, 'move-up.png', 'Move model up in print list')
|
||||
toolbarUtil.NormalButton(self.toolbar2, self.OnMoveDown, 'move-down.png', 'Move model down in print list')
|
||||
toolbarUtil.NormalButton(self.toolbar2, self.OnCopy, 'copy.png', 'Make a copy of the current selected object')
|
||||
self.toolbar2.Realize()
|
||||
|
||||
sizer = wx.GridBagSizer(2,2)
|
||||
self.SetSizer(sizer)
|
||||
@ -65,7 +179,8 @@ class projectPlanner(wx.Frame):
|
||||
self.sliceButton = wx.Button(self, -1, "Slice")
|
||||
self.autoPlaceButton = wx.Button(self, -1, "Auto Place")
|
||||
|
||||
sizer.Add(self.toolbar, (0,0), span=(1,3), flag=wx.EXPAND)
|
||||
sizer.Add(self.toolbar, (0,0), span=(1,1), flag=wx.EXPAND)
|
||||
sizer.Add(self.toolbar2, (0,1), span=(1,2), flag=wx.EXPAND)
|
||||
sizer.Add(self.preview, (1,0), span=(4,1), flag=wx.EXPAND)
|
||||
sizer.Add(self.listbox, (1,1), span=(1,2), flag=wx.EXPAND)
|
||||
sizer.Add(self.addButton, (2,1), span=(1,1))
|
||||
@ -95,6 +210,12 @@ class projectPlanner(wx.Frame):
|
||||
sizer.Add(self.scaleCtrl, (0,1), flag=wx.ALIGN_BOTTOM|wx.EXPAND)
|
||||
sizer.Add(wx.StaticText(panel, -1, 'Rotate'), (1,0), flag=wx.ALIGN_CENTER_VERTICAL)
|
||||
sizer.Add(self.rotateCtrl, (1,1), flag=wx.ALIGN_BOTTOM|wx.EXPAND)
|
||||
|
||||
if int(profile.getPreference('extruder_amount')) > 1:
|
||||
self.extruderCtrl = wx.ComboBox(panel, -1, '1', choices=map(str, range(1, int(profile.getPreference('extruder_amount'))+1)), style=wx.CB_DROPDOWN|wx.CB_READONLY)
|
||||
sizer.Add(wx.StaticText(panel, -1, 'Extruder'), (2,0), flag=wx.ALIGN_CENTER_VERTICAL)
|
||||
sizer.Add(self.extruderCtrl, (2,1), flag=wx.ALIGN_BOTTOM|wx.EXPAND)
|
||||
self.extruderCtrl.Bind(wx.EVT_COMBOBOX, self.OnExtruderChange)
|
||||
|
||||
self.scaleCtrl.Bind(wx.EVT_TEXT, self.OnScaleChange)
|
||||
self.rotateCtrl.Bind(wx.EVT_SPINCTRL, self.OnRotateChange)
|
||||
@ -126,6 +247,7 @@ class projectPlanner(wx.Frame):
|
||||
cp.set(section, 'flipZ', str(item.flipZ))
|
||||
cp.set(section, 'swapXZ', str(item.swapXZ))
|
||||
cp.set(section, 'swapYZ', str(item.swapYZ))
|
||||
cp.set(section, 'extruder', str(item.extruder+1))
|
||||
i += 1
|
||||
cp.write(open(dlg.GetPath(), "w"))
|
||||
dlg.Destroy()
|
||||
@ -142,18 +264,19 @@ class projectPlanner(wx.Frame):
|
||||
while cp.has_section('model_%d' % (i)):
|
||||
section = 'model_%d' % (i)
|
||||
|
||||
item = stl.stlModel()
|
||||
item.filename = unicode(cp.get(section, 'filename'), "utf-8")
|
||||
self.loadModelFile(item)
|
||||
item = ProjectObject(unicode(cp.get(section, 'filename'), "utf-8"))
|
||||
item.centerX = float(cp.get(section, 'centerX'))
|
||||
item.centerY = float(cp.get(section, 'centerY'))
|
||||
item.scale = float(cp.get(section, 'scale'))
|
||||
item.rotate = float(cp.get(section, 'rotate'))
|
||||
cp.get(section, 'flipX')
|
||||
cp.get(section, 'flipY')
|
||||
cp.get(section, 'flipZ')
|
||||
cp.get(section, 'swapXZ')
|
||||
cp.get(section, 'swapYZ')
|
||||
item.flipX = cp.get(section, 'flipX') == 'True'
|
||||
item.flipY = cp.get(section, 'flipY') == 'True'
|
||||
item.flipZ = cp.get(section, 'flipZ') == 'True'
|
||||
item.swapXZ = cp.get(section, 'swapXZ') == 'True'
|
||||
item.swapYZ = cp.get(section, 'swapYZ') == 'True'
|
||||
if cp.has_option(section, 'extruder'):
|
||||
item.extuder = int(cp.get(section, 'extruder'))-1
|
||||
item.updateModelTransform()
|
||||
i += 1
|
||||
|
||||
self.list.append(item)
|
||||
@ -161,6 +284,7 @@ class projectPlanner(wx.Frame):
|
||||
|
||||
self.listbox.SetSelection(len(self.list)-1)
|
||||
self.OnListSelect(None)
|
||||
self.preview.Refresh()
|
||||
|
||||
dlg.Destroy()
|
||||
|
||||
@ -184,39 +308,76 @@ class projectPlanner(wx.Frame):
|
||||
self.selection = self.list[self.listbox.GetSelection()]
|
||||
self.scaleCtrl.SetValue(str(self.selection.scale))
|
||||
self.rotateCtrl.SetValue(int(self.selection.rotate))
|
||||
if int(profile.getPreference('extruder_amount')) > 1:
|
||||
self.extruderCtrl.SetValue(str(self.selection.extruder+1))
|
||||
self.preview.Refresh()
|
||||
|
||||
|
||||
def OnAddModel(self, e):
|
||||
dlg=wx.FileDialog(self, "Open file to print", os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_OPEN|wx.FD_FILE_MUST_EXIST|wx.FD_MULTIPLE)
|
||||
dlg.SetWildcard("STL files (*.stl)|*.stl;*.STL")
|
||||
if dlg.ShowModal() == wx.ID_OK:
|
||||
for filename in dlg.GetPaths():
|
||||
item = stl.stlModel()
|
||||
item.filename=filename
|
||||
item = ProjectObject(filename)
|
||||
profile.putPreference('lastFile', item.filename)
|
||||
if not(os.path.exists(item.filename)):
|
||||
return
|
||||
self.loadModelFile(item)
|
||||
self.list.append(item)
|
||||
self.listbox.AppendAndEnsureVisible(os.path.split(item.filename)[1])
|
||||
self.listbox.SetSelection(len(self.list)-1)
|
||||
self.OnListSelect(None)
|
||||
self.selection = item
|
||||
self._updateListbox()
|
||||
self.preview.Refresh()
|
||||
dlg.Destroy()
|
||||
|
||||
def OnRemModel(self, e):
|
||||
if self.selection == None:
|
||||
return
|
||||
self.list.remove(self.selection)
|
||||
i = self.listbox.GetSelection()
|
||||
self.listbox.Delete(i)
|
||||
if len(self.list) > i:
|
||||
self.listbox.SetSelection(i)
|
||||
elif len(self.list) > 0:
|
||||
self.listbox.SetSelection(len(self.list) - 1)
|
||||
self.selection = None
|
||||
self.OnListSelect(None)
|
||||
self._updateListbox()
|
||||
self.preview.Refresh()
|
||||
|
||||
def OnMoveUp(self, e):
|
||||
if self.selection == None:
|
||||
return
|
||||
i = self.listbox.GetSelection()
|
||||
if i == 0:
|
||||
return
|
||||
self.list.remove(self.selection)
|
||||
self.list.insert(i-1, self.selection)
|
||||
self._updateListbox()
|
||||
self.preview.Refresh()
|
||||
|
||||
def OnMoveDown(self, e):
|
||||
if self.selection == None:
|
||||
return
|
||||
i = self.listbox.GetSelection()
|
||||
if i == len(self.list) - 1:
|
||||
return
|
||||
self.list.remove(self.selection)
|
||||
self.list.insert(i+1, self.selection)
|
||||
self._updateListbox()
|
||||
self.preview.Refresh()
|
||||
|
||||
def OnCopy(self, e):
|
||||
if self.selection == None:
|
||||
return
|
||||
|
||||
item = self.selection.clone()
|
||||
self.list.append(item)
|
||||
self.selection = item
|
||||
|
||||
self._updateListbox()
|
||||
self.preview.Refresh()
|
||||
|
||||
def _updateListbox(self):
|
||||
self.listbox.Clear()
|
||||
for item in self.list:
|
||||
self.listbox.AppendAndEnsureVisible(os.path.split(item.filename)[1])
|
||||
if self.selection in self.list:
|
||||
self.listbox.SetSelection(self.list.index(self.selection))
|
||||
elif len(self.list) > 0:
|
||||
self.selection = self.list[0]
|
||||
self.listbox.SetSelection(0)
|
||||
else:
|
||||
self.selection = None
|
||||
self.listbox.SetSelection(-1)
|
||||
|
||||
def OnAutoPlace(self, e):
|
||||
bestAllowedSize = int(self.machineSize.y)
|
||||
bestArea = self._doAutoPlace(bestAllowedSize)
|
||||
@ -233,7 +394,7 @@ class projectPlanner(wx.Frame):
|
||||
extraSizeMax = self.headSizeMax
|
||||
if profile.getProfileSettingFloat('skirt_line_count') > 0:
|
||||
skirtSize = profile.getProfileSettingFloat('skirt_line_count') * profile.calculateEdgeWidth() + profile.getProfileSettingFloat('skirt_gap')
|
||||
extraSizeMin = extraSizeMin - util3d.Vector3(skirtSize, skirtSize, 0)
|
||||
extraSizeMin = extraSizeMin + util3d.Vector3(skirtSize, skirtSize, 0)
|
||||
extraSizeMax = extraSizeMax + util3d.Vector3(skirtSize, skirtSize, 0)
|
||||
|
||||
posX = self.machineSize.x
|
||||
@ -268,9 +429,7 @@ class projectPlanner(wx.Frame):
|
||||
return (maxX - minX) + (maxY - minY)
|
||||
|
||||
def OnSlice(self, e):
|
||||
oldProfile = profile.getGlobalProfileString()
|
||||
|
||||
put = profile.putProfileSetting
|
||||
put = profile.setTempOverride
|
||||
|
||||
put('model_multiply_x', '1')
|
||||
put('model_multiply_y', '1')
|
||||
@ -281,8 +440,8 @@ class projectPlanner(wx.Frame):
|
||||
clearZ = 0
|
||||
actionList = []
|
||||
for item in self.list:
|
||||
put('machine_center_x', item.centerX)
|
||||
put('machine_center_y', item.centerY)
|
||||
put('machine_center_x', item.centerX - self.extruderOffset[item.extruder].x)
|
||||
put('machine_center_y', item.centerY - self.extruderOffset[item.extruder].y)
|
||||
put('model_scale', item.scale)
|
||||
put('flip_x', item.flipX)
|
||||
put('flip_y', item.flipY)
|
||||
@ -295,13 +454,14 @@ class projectPlanner(wx.Frame):
|
||||
action.sliceCmd = sliceRun.getSliceCommand(item.filename)
|
||||
action.centerX = item.centerX
|
||||
action.centerY = item.centerY
|
||||
action.extruder = item.extruder
|
||||
action.filename = item.filename
|
||||
clearZ = max(clearZ, item.getMaximum().z * item.scale)
|
||||
action.clearZ = clearZ
|
||||
actionList.append(action)
|
||||
|
||||
#Restore the old profile.
|
||||
profile.loadGlobalProfileFromString(oldProfile)
|
||||
profile.resetTempOverride()
|
||||
|
||||
dlg=wx.FileDialog(self, "Save project gcode file", os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_SAVE)
|
||||
dlg.SetWildcard("GCode file (*.gcode)|*.gcode")
|
||||
@ -312,34 +472,10 @@ class projectPlanner(wx.Frame):
|
||||
dlg.Destroy()
|
||||
|
||||
pspw = ProjectSliceProgressWindow(actionList, resultFilename)
|
||||
pspw.extruderOffset = self.extruderOffset
|
||||
pspw.Centre()
|
||||
pspw.Show(True)
|
||||
|
||||
def loadModelFile(self, item):
|
||||
item.load(item.filename)
|
||||
item.origonalVertexes = list(item.vertexes)
|
||||
for i in xrange(0, len(item.origonalVertexes)):
|
||||
item.origonalVertexes[i] = item.origonalVertexes[i].copy()
|
||||
item.getMinimumZ()
|
||||
|
||||
item.centerX = -item.getMinimum().x + 5
|
||||
item.centerY = -item.getMinimum().y + 5
|
||||
item.scale = 1.0
|
||||
item.rotate = 0.0
|
||||
item.flipX = False
|
||||
item.flipY = False
|
||||
item.flipZ = False
|
||||
item.swapXZ = False
|
||||
item.swapYZ = False
|
||||
|
||||
item.modelDisplayList = None
|
||||
item.modelDirty = False
|
||||
|
||||
self.updateModelTransform(item)
|
||||
|
||||
item.centerX = -item.getMinimum().x + 5
|
||||
item.centerY = -item.getMinimum().y + 5
|
||||
|
||||
def OnScaleChange(self, e):
|
||||
if self.selection == None:
|
||||
return
|
||||
@ -353,57 +489,13 @@ class projectPlanner(wx.Frame):
|
||||
if self.selection == None:
|
||||
return
|
||||
self.selection.rotate = float(self.rotateCtrl.GetValue())
|
||||
self.updateModelTransform(self.selection)
|
||||
self.selection.updateModelTransform()
|
||||
self.preview.Refresh()
|
||||
|
||||
def updateModelTransform(self, item):
|
||||
rotate = item.rotate / 180.0 * math.pi
|
||||
scaleX = 1.0
|
||||
scaleY = 1.0
|
||||
scaleZ = 1.0
|
||||
if item.flipX:
|
||||
scaleX = -scaleX
|
||||
if item.flipY:
|
||||
scaleY = -scaleY
|
||||
if item.flipZ:
|
||||
scaleZ = -scaleZ
|
||||
swapXZ = item.swapXZ
|
||||
swapYZ = item.swapYZ
|
||||
mat00 = math.cos(rotate) * scaleX
|
||||
mat01 =-math.sin(rotate) * scaleY
|
||||
mat10 = math.sin(rotate) * scaleX
|
||||
mat11 = math.cos(rotate) * scaleY
|
||||
|
||||
for i in xrange(0, len(item.origonalVertexes)):
|
||||
x = item.origonalVertexes[i].x
|
||||
y = item.origonalVertexes[i].y
|
||||
z = item.origonalVertexes[i].z
|
||||
if swapXZ:
|
||||
x, z = z, x
|
||||
if swapYZ:
|
||||
y, z = z, y
|
||||
item.vertexes[i].x = x * mat00 + y * mat01
|
||||
item.vertexes[i].y = x * mat10 + y * mat11
|
||||
item.vertexes[i].z = z * scaleZ
|
||||
|
||||
for face in item.faces:
|
||||
v1 = face.v[0]
|
||||
v2 = face.v[1]
|
||||
v3 = face.v[2]
|
||||
face.normal = (v2 - v1).cross(v3 - v1)
|
||||
face.normal.normalize()
|
||||
|
||||
self.moveModel(item)
|
||||
|
||||
def moveModel(self, item):
|
||||
minZ = item.getMinimumZ()
|
||||
min = item.getMinimum()
|
||||
max = item.getMaximum()
|
||||
for v in item.vertexes:
|
||||
v.z -= minZ
|
||||
v.x -= min.x + (max.x - min.x) / 2
|
||||
v.y -= min.y + (max.y - min.y) / 2
|
||||
item.getMinimumZ()
|
||||
item.modelDirty = True
|
||||
def OnExtruderChange(self, e):
|
||||
if self.selection == None:
|
||||
return
|
||||
self.selection.extruder = int(self.extruderCtrl.GetValue()) - 1
|
||||
self.preview.Refresh()
|
||||
|
||||
class PreviewGLCanvas(glcanvas.GLCanvas):
|
||||
@ -445,14 +537,14 @@ class PreviewGLCanvas(glcanvas.GLCanvas):
|
||||
if item != None:
|
||||
item.centerX += float(e.GetX() - self.oldX) * self.zoom / self.GetSize().GetHeight() * 2
|
||||
item.centerY -= float(e.GetY() - self.oldY) * self.zoom / self.GetSize().GetHeight() * 2
|
||||
if item.centerX < -item.getMinimum().x * item.scale:
|
||||
item.centerX = -item.getMinimum().x * item.scale
|
||||
if item.centerY < -item.getMinimum().y * item.scale:
|
||||
item.centerY = -item.getMinimum().y * item.scale
|
||||
if item.centerX > self.parent.machineSize.x - item.getMaximum().x * item.scale:
|
||||
item.centerX = self.parent.machineSize.x - item.getMaximum().x * item.scale
|
||||
if item.centerY > self.parent.machineSize.y - item.getMaximum().y * item.scale:
|
||||
item.centerY = self.parent.machineSize.y - item.getMaximum().y * item.scale
|
||||
if item.centerX < -item.getMinimum().x * item.scale + self.parent.extruderOffset[item.extruder].x:
|
||||
item.centerX = -item.getMinimum().x * item.scale + self.parent.extruderOffset[item.extruder].x
|
||||
if item.centerY < -item.getMinimum().y * item.scale + self.parent.extruderOffset[item.extruder].y:
|
||||
item.centerY = -item.getMinimum().y * item.scale + self.parent.extruderOffset[item.extruder].y
|
||||
if item.centerX > self.parent.machineSize.x + self.parent.extruderOffset[item.extruder].x - item.getMaximum().x * item.scale:
|
||||
item.centerX = self.parent.machineSize.x + self.parent.extruderOffset[item.extruder].x - item.getMaximum().x * item.scale
|
||||
if item.centerY > self.parent.machineSize.y + self.parent.extruderOffset[item.extruder].y - item.getMaximum().y * item.scale:
|
||||
item.centerY = self.parent.machineSize.y + self.parent.extruderOffset[item.extruder].y - item.getMaximum().y * item.scale
|
||||
self.Refresh()
|
||||
else:
|
||||
self.allowDrag = False
|
||||
@ -508,7 +600,7 @@ class PreviewGLCanvas(glcanvas.GLCanvas):
|
||||
extraSizeMax = self.parent.headSizeMax
|
||||
if profile.getProfileSettingFloat('skirt_line_count') > 0:
|
||||
skirtSize = profile.getProfileSettingFloat('skirt_line_count') * profile.calculateEdgeWidth() + profile.getProfileSettingFloat('skirt_gap')
|
||||
extraSizeMin = extraSizeMin - util3d.Vector3(skirtSize, skirtSize, 0)
|
||||
extraSizeMin = extraSizeMin + util3d.Vector3(skirtSize, skirtSize, 0)
|
||||
extraSizeMax = extraSizeMax + util3d.Vector3(skirtSize, skirtSize, 0)
|
||||
|
||||
for item in self.parent.list:
|
||||
@ -517,8 +609,8 @@ class PreviewGLCanvas(glcanvas.GLCanvas):
|
||||
|
||||
for idx1 in xrange(0, len(self.parent.list)):
|
||||
item = self.parent.list[idx1]
|
||||
iMin1 = item.getMinimum() * item.scale + util3d.Vector3(item.centerX, item.centerY, 0) - extraSizeMin
|
||||
iMax1 = item.getMaximum() * item.scale + util3d.Vector3(item.centerX, item.centerY, 0) + extraSizeMax
|
||||
iMin1 = item.getMinimum() * item.scale + util3d.Vector3(item.centerX, item.centerY, 0) - extraSizeMin - self.parent.extruderOffset[item.extruder]
|
||||
iMax1 = item.getMaximum() * item.scale + util3d.Vector3(item.centerX, item.centerY, 0) + extraSizeMax - self.parent.extruderOffset[item.extruder]
|
||||
for idx2 in xrange(0, idx1):
|
||||
item2 = self.parent.list[idx2]
|
||||
iMin2 = item2.getMinimum() * item2.scale + util3d.Vector3(item2.centerX, item2.centerY, 0)
|
||||
@ -566,8 +658,8 @@ class PreviewGLCanvas(glcanvas.GLCanvas):
|
||||
|
||||
vMin = item.getMinimum() * item.scale
|
||||
vMax = item.getMaximum() * item.scale
|
||||
vMinHead = vMin - extraSizeMin
|
||||
vMaxHead = vMax + extraSizeMax
|
||||
vMinHead = vMin - extraSizeMin - self.parent.extruderOffset[item.extruder]
|
||||
vMaxHead = vMax + extraSizeMax - self.parent.extruderOffset[item.extruder]
|
||||
|
||||
glDisable(GL_LIGHTING)
|
||||
|
||||
@ -612,28 +704,6 @@ class ProjectSliceProgressWindow(wx.Frame):
|
||||
self.startTime = time.time()
|
||||
self.sliceStartTime = time.time()
|
||||
|
||||
#How long does each step take compared to the others. This is used to make a better scaled progress bar, and guess time left.
|
||||
# TODO: Duplicate with sliceProgressPanel, move to sliceRun.
|
||||
self.sliceStepTimeFactor = {
|
||||
'start': 3.3713991642,
|
||||
'slice': 15.4984838963,
|
||||
'preface': 5.17178297043,
|
||||
'inset': 116.362634182,
|
||||
'fill': 215.702672005,
|
||||
'multiply': 21.9536788464,
|
||||
'speed': 12.759510994,
|
||||
'raft': 31.4580039978,
|
||||
'skirt': 19.3436040878,
|
||||
'skin': 1.0,
|
||||
'joris': 1.0,
|
||||
'comb': 23.7805759907,
|
||||
'cool': 27.148763895,
|
||||
'dimension': 90.4914340973
|
||||
}
|
||||
self.totalRunTimeFactor = 0
|
||||
for v in self.sliceStepTimeFactor.itervalues():
|
||||
self.totalRunTimeFactor += v
|
||||
|
||||
self.sizer = wx.GridBagSizer(2, 2)
|
||||
self.statusText = wx.StaticText(self, -1, "Building: %s" % (resultFilename))
|
||||
self.progressGauge = wx.Gauge(self, -1)
|
||||
@ -644,6 +714,7 @@ class ProjectSliceProgressWindow(wx.Frame):
|
||||
self.sizer.Add(self.statusText, (0,0), flag=wx.ALIGN_CENTER)
|
||||
self.sizer.Add(self.progressGauge, (1, 0), flag=wx.EXPAND)
|
||||
self.sizer.Add(self.progressGauge2, (2, 0), flag=wx.EXPAND)
|
||||
|
||||
self.sizer.Add(self.abortButton, (3,0), flag=wx.ALIGN_CENTER)
|
||||
self.sizer.AddGrowableCol(0)
|
||||
self.sizer.AddGrowableRow(0)
|
||||
@ -664,19 +735,19 @@ class ProjectSliceProgressWindow(wx.Frame):
|
||||
|
||||
def SetProgress(self, stepName, layer, maxLayer):
|
||||
if self.prevStep != stepName:
|
||||
self.totalDoneFactor += self.sliceStepTimeFactor[self.prevStep]
|
||||
self.totalDoneFactor += sliceRun.sliceStepTimeFactor[self.prevStep]
|
||||
newTime = time.time()
|
||||
#print "#####" + str(newTime-self.startTime) + " " + self.prevStep + " -> " + stepName
|
||||
self.startTime = newTime
|
||||
self.prevStep = stepName
|
||||
|
||||
progresValue = ((self.totalDoneFactor + self.sliceStepTimeFactor[stepName] * layer / maxLayer) / self.totalRunTimeFactor) * 10000
|
||||
progresValue = ((self.totalDoneFactor + sliceRun.sliceStepTimeFactor[stepName] * layer / maxLayer) / sliceRun.totalRunTimeFactor) * 10000
|
||||
self.progressGauge.SetValue(int(progresValue))
|
||||
self.statusText.SetLabel(stepName + " [" + str(layer) + "/" + str(maxLayer) + "]")
|
||||
|
||||
def OnRun(self):
|
||||
resultFile = open(self.resultFilename, "w")
|
||||
put = profile.putProfileSetting
|
||||
put = profile.setTempOverride
|
||||
for action in self.actionList:
|
||||
p = subprocess.Popen(action.sliceCmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
line = p.stdout.readline()
|
||||
@ -697,24 +768,27 @@ class ProjectSliceProgressWindow(wx.Frame):
|
||||
if self.abort:
|
||||
p.terminate()
|
||||
wx.CallAfter(self.statusText.SetLabel, "Aborted by user.")
|
||||
resultFile.close()
|
||||
return
|
||||
line = p.stdout.readline()
|
||||
self.returnCode = p.wait()
|
||||
|
||||
oldProfile = profile.getGlobalProfileString()
|
||||
put('machine_center_x', action.centerX)
|
||||
put('machine_center_y', action.centerY)
|
||||
put('machine_center_x', action.centerX - self.extruderOffset[action.extruder].x)
|
||||
put('machine_center_y', action.centerY - self.extruderOffset[action.extruder].y)
|
||||
put('clear_z', action.clearZ)
|
||||
put('extruder', action.extruder)
|
||||
|
||||
if action == self.actionList[0]:
|
||||
resultFile.write(';TYPE:CUSTOM\n')
|
||||
resultFile.write('T%d\n' % (action.extruder))
|
||||
currentExtruder = action.extruder
|
||||
resultFile.write(profile.getAlterationFileContents('start.gcode'))
|
||||
else:
|
||||
#reset the extrusion length, and move to the next object center.
|
||||
resultFile.write(';TYPE:CUSTOM\n')
|
||||
resultFile.write(profile.getAlterationFileContents('nextobject.gcode'))
|
||||
resultFile.write(';PRINTNR:%d\n' % self.actionList.index(action))
|
||||
profile.loadGlobalProfileFromString(oldProfile)
|
||||
profile.resetTempOverride()
|
||||
|
||||
f = open(action.filename[: action.filename.rfind('.')] + "_export.project_tmp", "r")
|
||||
data = f.read(4096)
|
||||
@ -731,9 +805,20 @@ class ProjectSliceProgressWindow(wx.Frame):
|
||||
resultFile.write(';TYPE:CUSTOM\n')
|
||||
resultFile.write(profile.getAlterationFileContents('end.gcode'))
|
||||
resultFile.close()
|
||||
|
||||
gcode = gcodeInterpreter.gcode()
|
||||
gcode.load(self.resultFilename)
|
||||
|
||||
self.abort = True
|
||||
sliceTime = time.time() - self.sliceStartTime
|
||||
wx.CallAfter(self.statusText.SetLabel, 'Slicing took: %d:%d' % (sliceTime / 60, sliceTime % 60))
|
||||
status = "Slicing took: %02d:%02d\n" % (sliceTime / 60, sliceTime % 60)
|
||||
status = "Filament: %.2fm %.2fg\n" % (gcode.extrusionAmount / 1000, gcode.calculateWeight() * 1000)
|
||||
status += "Print time: %02d:%02d\n" % (int(gcode.totalMoveTimeMinute / 60), int(gcode.totalMoveTimeMinute % 60))
|
||||
cost = gcode.calculateCost()
|
||||
if cost != False:
|
||||
status += "Cost: %s\n" % (cost)
|
||||
wx.CallAfter(self.statusText.SetLabel, status)
|
||||
|
||||
wx.CallAfter(self.abortButton.SetLabel, 'Close')
|
||||
|
||||
def main():
|
||||
|
@ -117,7 +117,7 @@ class simpleModeWindow(configBase.configWindowBase):
|
||||
self.sizer = sizer
|
||||
|
||||
if self.filename != "None":
|
||||
self.preview3d.loadModelFile(self.filename)
|
||||
self.preview3d.loadModelFiles([self.filename])
|
||||
self.lastPath = os.path.split(self.filename)[0]
|
||||
|
||||
self.updateProfileToControls()
|
||||
@ -158,7 +158,7 @@ class simpleModeWindow(configBase.configWindowBase):
|
||||
if not(os.path.exists(self.filename)):
|
||||
return
|
||||
self.lastPath = os.path.split(self.filename)[0]
|
||||
self.preview3d.loadModelFile(self.filename)
|
||||
self.preview3d.loadModelFiles([self.filename])
|
||||
self.preview3d.setViewMode("Normal")
|
||||
dlg.Destroy()
|
||||
|
||||
|
@ -1,11 +1,12 @@
|
||||
from __future__ import absolute_import
|
||||
import __init__
|
||||
|
||||
import wx, sys, os, math, threading, subprocess, time
|
||||
import wx, sys, os, math, threading, subprocess, time, re
|
||||
|
||||
from util import profile
|
||||
from util import sliceRun
|
||||
from util import exporer
|
||||
from util import gcodeInterpreter
|
||||
|
||||
class sliceProgessPanel(wx.Panel):
|
||||
def __init__(self, mainWindow, parent, filelist):
|
||||
@ -14,27 +15,6 @@ class sliceProgessPanel(wx.Panel):
|
||||
self.filelist = filelist
|
||||
self.abort = False
|
||||
|
||||
#How long does each step take compared to the others. This is used to make a better scaled progress bar, and guess time left.
|
||||
self.sliceStepTimeFactor = {
|
||||
'start': 3.3713991642,
|
||||
'slice': 15.4984838963,
|
||||
'preface': 5.17178297043,
|
||||
'inset': 116.362634182,
|
||||
'fill': 215.702672005,
|
||||
'multiply': 21.9536788464,
|
||||
'speed': 12.759510994,
|
||||
'raft': 31.4580039978,
|
||||
'skirt': 19.3436040878,
|
||||
'skin': 1.0,
|
||||
'joris': 1.0,
|
||||
'comb': 23.7805759907,
|
||||
'cool': 27.148763895,
|
||||
'dimension': 90.4914340973
|
||||
}
|
||||
self.totalRunTimeFactor = 0
|
||||
for v in self.sliceStepTimeFactor.itervalues():
|
||||
self.totalRunTimeFactor += v
|
||||
|
||||
box = wx.StaticBox(self, -1, filelist[0])
|
||||
self.sizer = wx.StaticBoxSizer(box, wx.HORIZONTAL)
|
||||
|
||||
@ -58,18 +38,20 @@ class sliceProgessPanel(wx.Panel):
|
||||
if profile.getPreference('save_profile') == 'True':
|
||||
profile.saveGlobalProfile(self.filelist[0][: self.filelist[0].rfind('.')] + "_profile.ini")
|
||||
cmdList = []
|
||||
oldProfile = profile.getGlobalProfileString()
|
||||
for filename in self.filelist:
|
||||
print filename, self.filelist.index(filename)
|
||||
if self.filelist.index(filename) > 0:
|
||||
profile.putProfileSetting('fan_enabled', 'False')
|
||||
profile.putProfileSetting('skirt_line_count', '0')
|
||||
profile.putProfileSetting('machine_center_x', profile.getProfileSettingFloat('machine_center_x') + 22)
|
||||
idx = self.filelist.index(filename)
|
||||
print filename, idx
|
||||
if idx > 0:
|
||||
profile.setTempOverride('fan_enabled', 'False')
|
||||
profile.setTempOverride('skirt_line_count', '0')
|
||||
profile.setTempOverride('machine_center_x', profile.getProfileSettingFloat('machine_center_x') - profile.getPreferenceFloat('extruder_offset_x%d' % (idx)))
|
||||
profile.setTempOverride('machine_center_y', profile.getProfileSettingFloat('machine_center_y') - profile.getPreferenceFloat('extruder_offset_y%d' % (idx)))
|
||||
profile.setTempOverride('alternative_center', self.filelist[0])
|
||||
if len(self.filelist) > 1:
|
||||
profile.putProfileSetting('add_start_end_gcode', 'False')
|
||||
profile.putProfileSetting('gcode_extension', 'multi_extrude_tmp')
|
||||
profile.setTempOverride('add_start_end_gcode', 'False')
|
||||
profile.setTempOverride('gcode_extension', 'multi_extrude_tmp')
|
||||
cmdList.append(sliceRun.getSliceCommand(filename))
|
||||
profile.loadGlobalProfileFromString(oldProfile)
|
||||
profile.resetTempOverride()
|
||||
self.thread = WorkerThread(self, filelist, cmdList)
|
||||
|
||||
def OnAbort(self, e):
|
||||
@ -98,7 +80,12 @@ class sliceProgessPanel(wx.Panel):
|
||||
self.Bind(wx.EVT_BUTTON, self.OnAbort, self.abortButton)
|
||||
self.sizer.Add(self.logButton, 0)
|
||||
if result.returnCode == 0:
|
||||
self.statusText.SetLabel("Ready.")
|
||||
status = "Ready: Filament: %.2fm %.2fg" % (result.gcode.extrusionAmount / 1000, result.gcode.calculateWeight() * 1000)
|
||||
status += " Print time: %02d:%02d" % (int(result.gcode.totalMoveTimeMinute / 60), int(result.gcode.totalMoveTimeMinute % 60))
|
||||
cost = result.gcode.calculateCost()
|
||||
if cost != False:
|
||||
status += " Cost: %s" % (cost)
|
||||
self.statusText.SetLabel(status)
|
||||
if exporer.hasExporer():
|
||||
self.openFileLocationButton = wx.Button(self, -1, "Open file location")
|
||||
self.Bind(wx.EVT_BUTTON, self.OnOpenFileLocation, self.openFileLocationButton)
|
||||
@ -117,13 +104,13 @@ class sliceProgessPanel(wx.Panel):
|
||||
|
||||
def SetProgress(self, stepName, layer, maxLayer):
|
||||
if self.prevStep != stepName:
|
||||
self.totalDoneFactor += self.sliceStepTimeFactor[self.prevStep]
|
||||
self.totalDoneFactor += sliceRun.sliceStepTimeFactor[self.prevStep]
|
||||
newTime = time.time()
|
||||
#print "#####" + str(newTime-self.startTime) + " " + self.prevStep + " -> " + stepName
|
||||
self.startTime = newTime
|
||||
self.prevStep = stepName
|
||||
|
||||
progresValue = ((self.totalDoneFactor + self.sliceStepTimeFactor[stepName] * layer / maxLayer) / self.totalRunTimeFactor) * 10000
|
||||
progresValue = ((self.totalDoneFactor + sliceRun.sliceStepTimeFactor[stepName] * layer / maxLayer) / sliceRun.totalRunTimeFactor) * 10000
|
||||
self.progressGauge.SetValue(int(progresValue))
|
||||
self.statusText.SetLabel(stepName + " [" + str(layer) + "/" + str(maxLayer) + "]")
|
||||
|
||||
@ -167,6 +154,8 @@ class WorkerThread(threading.Thread):
|
||||
if self.fileIdx == len(self.cmdList):
|
||||
if len(self.filelist) > 1:
|
||||
self._stitchMultiExtruder()
|
||||
self.gcode = gcodeInterpreter.gcode()
|
||||
self.gcode.load(self.filelist[0][:self.filelist[0].rfind('.')]+'_export.gcode')
|
||||
wx.CallAfter(self.notifyWindow.OnSliceDone, self)
|
||||
else:
|
||||
self.run()
|
||||
@ -177,10 +166,13 @@ class WorkerThread(threading.Thread):
|
||||
resultFile.write(';TYPE:CUSTOM\n')
|
||||
resultFile.write(profile.getAlterationFileContents('start.gcode'))
|
||||
for filename in self.filelist:
|
||||
files.append(open(filename[:filename.rfind('.')]+'_export.multi_extrude_tmp', "r"))
|
||||
if os.path.isfile(filename[:filename.rfind('.')]+'_export.multi_extrude_tmp'):
|
||||
files.append(open(filename[:filename.rfind('.')]+'_export.multi_extrude_tmp', "r"))
|
||||
else:
|
||||
return
|
||||
|
||||
currentExtruder = 0
|
||||
resultFile.write("T%d\n" % (currentExtruder))
|
||||
resultFile.write('T%d\n' % (currentExtruder))
|
||||
layerNr = -1
|
||||
hasLine = True
|
||||
while hasLine:
|
||||
@ -191,14 +183,17 @@ class WorkerThread(threading.Thread):
|
||||
hasLine = True
|
||||
if line.startswith(';LAYER:'):
|
||||
break
|
||||
if 'Z' in line:
|
||||
lastZ = float(re.search('Z([^\s]+)', line).group(1))
|
||||
if not layerHasLine:
|
||||
nextExtruder = files.index(f)
|
||||
resultFile.write(';LAYER:%d\n' % (layerNr))
|
||||
resultFile.write(';EXTRUDER:%d\n' % (nextExtruder))
|
||||
if nextExtruder != currentExtruder:
|
||||
resultFile.write("G1 E-2 F3000\n")
|
||||
resultFile.write("G1 E-5 F5000\n")
|
||||
resultFile.write("G92 E0\n")
|
||||
resultFile.write("T%d\n" % (nextExtruder))
|
||||
resultFile.write("G1 E2 F3000\n")
|
||||
resultFile.write("G1 E5 F5000\n")
|
||||
resultFile.write("G92 E0\n")
|
||||
currentExtruder = nextExtruder
|
||||
layerHasLine = True
|
||||
|
@ -180,6 +180,7 @@ class NormalButton(buttons.GenBitmapButton):
|
||||
super(NormalButton, self).__init__(parent, id, self.bitmap, size=size)
|
||||
|
||||
self.helpText = helpText
|
||||
self.callback = callback
|
||||
|
||||
self.SetBezelWidth(1)
|
||||
self.SetUseFocusIndicator(False)
|
||||
@ -187,10 +188,14 @@ class NormalButton(buttons.GenBitmapButton):
|
||||
self.Bind(wx.EVT_ENTER_WINDOW, self.OnMouseEnter)
|
||||
self.Bind(wx.EVT_LEAVE_WINDOW, self.OnMouseLeave)
|
||||
|
||||
self.Bind(wx.EVT_BUTTON, callback)
|
||||
self.Bind(wx.EVT_BUTTON, self.OnButton)
|
||||
|
||||
parent.AddControl(self)
|
||||
|
||||
def OnButton(self, event):
|
||||
self.GetParent().OnPopupHide(event)
|
||||
self.callback(event)
|
||||
|
||||
def OnMouseEnter(self, event):
|
||||
self.GetParent().OnPopupDisplay(event)
|
||||
event.Skip()
|
||||
|
@ -11,7 +11,7 @@ SUCCESS = 0
|
||||
WARNING = 1
|
||||
ERROR = 2
|
||||
|
||||
class validFloat():
|
||||
class validFloat(object):
|
||||
def __init__(self, setting, minValue = None, maxValue = None):
|
||||
self.setting = setting
|
||||
self.setting.validators.append(self)
|
||||
@ -29,7 +29,7 @@ class validFloat():
|
||||
except (ValueError, SyntaxError):
|
||||
return ERROR, '"' + str(self.setting.GetValue()) + '" is not a valid number or expression'
|
||||
|
||||
class validInt():
|
||||
class validInt(object):
|
||||
def __init__(self, setting, minValue = None, maxValue = None):
|
||||
self.setting = setting
|
||||
self.setting.validators.append(self)
|
||||
@ -47,7 +47,7 @@ class validInt():
|
||||
except (ValueError, SyntaxError):
|
||||
return ERROR, '"' + str(self.setting.GetValue()) + '" is not a valid whole number or expression'
|
||||
|
||||
class warningAbove():
|
||||
class warningAbove(object):
|
||||
def __init__(self, setting, minValueForWarning, warningMessage):
|
||||
self.setting = setting
|
||||
self.setting.validators.append(self)
|
||||
@ -68,7 +68,7 @@ class warningAbove():
|
||||
#We already have an error by the int/float validator in this case.
|
||||
return SUCCESS, ''
|
||||
|
||||
class wallThicknessValidator():
|
||||
class wallThicknessValidator(object):
|
||||
def __init__(self, setting):
|
||||
self.setting = setting
|
||||
self.setting.validators.append(self)
|
||||
@ -94,7 +94,7 @@ class wallThicknessValidator():
|
||||
#We already have an error by the int/float validator in this case.
|
||||
return SUCCESS, ''
|
||||
|
||||
class printSpeedValidator():
|
||||
class printSpeedValidator(object):
|
||||
def __init__(self, setting):
|
||||
self.setting = setting
|
||||
self.setting.validators.append(self)
|
||||
|
BIN
Cura/images/copy.png
Normal file
BIN
Cura/images/copy.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 485 B |
BIN
Cura/images/move-down.png
Normal file
BIN
Cura/images/move-down.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 623 B |
BIN
Cura/images/move-up.png
Normal file
BIN
Cura/images/move-up.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 672 B |
BIN
Cura/images/object-add.png
Normal file
BIN
Cura/images/object-add.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 926 B |
BIN
Cura/images/object-remove.png
Normal file
BIN
Cura/images/object-remove.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 941 B |
@ -9,13 +9,13 @@ import os
|
||||
from util import util3d
|
||||
from util import profile
|
||||
|
||||
class gcodePath():
|
||||
class gcodePath(object):
|
||||
def __init__(self, newType, pathType, startPoint):
|
||||
self.type = newType
|
||||
self.pathType = pathType
|
||||
self.list = [startPoint]
|
||||
|
||||
class gcode():
|
||||
class gcode(object):
|
||||
def __init__(self):
|
||||
self.regMatch = {}
|
||||
self.layerList = []
|
||||
@ -36,7 +36,18 @@ class gcode():
|
||||
#Calculates the weight of the filament in kg
|
||||
radius = float(profile.getProfileSetting('filament_diameter')) / 2
|
||||
volumeM3 = (self.extrusionAmount * (math.pi * radius * radius)) / (1000*1000*1000)
|
||||
return volumeM3 * float(profile.getPreference('filament_density'))
|
||||
return volumeM3 * profile.getPreferenceFloat('filament_density')
|
||||
|
||||
def calculateCost(self):
|
||||
cost_kg = profile.getPreferenceFloat('filament_cost_kg')
|
||||
cost_meter = profile.getPreferenceFloat('filament_cost_meter')
|
||||
if cost_kg > 0.0 and cost_meter > 0.0:
|
||||
return "%.2f / %.2f" % (self.calculateWeight() * cost_kg, self.extrusionAmount / 1000 * cost_meter)
|
||||
elif cost_kg > 0.0:
|
||||
return "%.2f" % (self.calculateWeight() * cost_kg)
|
||||
elif cost_meter > 0.0:
|
||||
return "%.2f" % (self.extrusionAmount / 1000 * cost_meter)
|
||||
return False
|
||||
|
||||
def _load(self, gcodeFile):
|
||||
filePos = 0
|
||||
@ -45,6 +56,7 @@ class gcode():
|
||||
currentE = 0.0
|
||||
totalExtrusion = 0.0
|
||||
maxExtrusion = 0.0
|
||||
currentExtruder = 0
|
||||
totalMoveTimeMinute = 0.0
|
||||
scale = 1.0
|
||||
posAbs = True
|
||||
@ -79,6 +91,15 @@ class gcode():
|
||||
if pathType != "CUSTOM":
|
||||
startCodeDone = True
|
||||
line = line[0:line.find(';')]
|
||||
T = self.getCodeInt(line, 'T')
|
||||
if T is not None:
|
||||
if currentExtruder > 0:
|
||||
posOffset.x -= profile.getPreferenceFloat('extruder_offset_x%d' % (currentExtruder))
|
||||
posOffset.y -= profile.getPreferenceFloat('extruder_offset_y%d' % (currentExtruder))
|
||||
currentExtruder = T
|
||||
if currentExtruder > 0:
|
||||
posOffset.x += profile.getPreferenceFloat('extruder_offset_x%d' % (currentExtruder))
|
||||
posOffset.y += profile.getPreferenceFloat('extruder_offset_y%d' % (currentExtruder))
|
||||
|
||||
G = self.getCodeInt(line, 'G')
|
||||
if G is not None:
|
||||
@ -91,17 +112,17 @@ class gcode():
|
||||
oldPos = pos.copy()
|
||||
if x is not None:
|
||||
if posAbs:
|
||||
pos.x = x * scale
|
||||
pos.x = x * scale + posOffset.x
|
||||
else:
|
||||
pos.x += x * scale
|
||||
if y is not None:
|
||||
if posAbs:
|
||||
pos.y = y * scale
|
||||
pos.y = y * scale + posOffset.y
|
||||
else:
|
||||
pos.y += y * scale
|
||||
if z is not None:
|
||||
if posAbs:
|
||||
pos.z = z * scale
|
||||
pos.z = z * scale + posOffset.z
|
||||
else:
|
||||
pos.z += z * scale
|
||||
#Check if we have a new layer.
|
||||
@ -200,8 +221,8 @@ class gcode():
|
||||
self.layerList.append(currentLayer)
|
||||
self.extrusionAmount = maxExtrusion
|
||||
self.totalMoveTimeMinute = totalMoveTimeMinute
|
||||
print "Extruded a total of: %d mm of filament" % (self.extrusionAmount)
|
||||
print "Estimated print duration: %.2f minutes" % (self.totalMoveTimeMinute)
|
||||
#print "Extruded a total of: %d mm of filament" % (self.extrusionAmount)
|
||||
#print "Estimated print duration: %.2f minutes" % (self.totalMoveTimeMinute)
|
||||
|
||||
def getCodeInt(self, line, code):
|
||||
if code not in self.regMatch:
|
||||
|
@ -34,7 +34,7 @@ profileDefaultSettings = {
|
||||
'bottom_layer_speed': '20',
|
||||
'cool_min_layer_time': '10',
|
||||
'fan_enabled': 'True',
|
||||
'fan_layer': '0',
|
||||
'fan_layer': '1',
|
||||
'fan_speed': '100',
|
||||
'model_scale': '1.0',
|
||||
'flip_x': 'False',
|
||||
@ -67,6 +67,9 @@ profileDefaultSettings = {
|
||||
|
||||
'add_start_end_gcode': 'True',
|
||||
'gcode_extension': 'gcode',
|
||||
'alternative_center': '',
|
||||
'clear_z': '0.0',
|
||||
'extruder': '0',
|
||||
}
|
||||
alterationDefault = {
|
||||
#######################################################################################
|
||||
@ -128,6 +131,12 @@ preferencesDefaultSettings = {
|
||||
'machine_depth': '205',
|
||||
'machine_height': '200',
|
||||
'extruder_amount': '1',
|
||||
'extruder_offset_x1': '-22.0',
|
||||
'extruder_offset_y1': '0.0',
|
||||
'extruder_offset_x2': '0.0',
|
||||
'extruder_offset_y2': '0.0',
|
||||
'extruder_offset_x3': '0.0',
|
||||
'extruder_offset_y3': '0.0',
|
||||
'filament_density': '1300',
|
||||
'steps_per_e': '0',
|
||||
'serial_port': 'AUTO',
|
||||
@ -136,12 +145,18 @@ preferencesDefaultSettings = {
|
||||
'save_profile': 'False',
|
||||
'filament_cost_kg': '0',
|
||||
'filament_cost_meter': '0',
|
||||
|
||||
'extruder_head_size_min_x': '70.0',
|
||||
'extruder_head_size_min_y': '18.0',
|
||||
'extruder_head_size_max_x': '18.0',
|
||||
'extruder_head_size_max_y': '35.0',
|
||||
}
|
||||
|
||||
#########################################################
|
||||
## Profile and preferences functions
|
||||
#########################################################
|
||||
|
||||
## Profile functions
|
||||
def getDefaultProfilePath():
|
||||
return os.path.normpath(os.path.join(os.path.dirname(os.path.abspath(__file__)), "../current_profile.ini"))
|
||||
|
||||
@ -177,15 +192,31 @@ def getGlobalProfileString():
|
||||
|
||||
p = []
|
||||
alt = []
|
||||
for key in globalProfileParser.options('profile'):
|
||||
p.append(key + "=" + globalProfileParser.get('profile', key))
|
||||
for key in globalProfileParser.options('alterations'):
|
||||
alt.append(key + "=" + globalProfileParser.get('alterations', key))
|
||||
tempDone = []
|
||||
if globalProfileParser.has_section('profile'):
|
||||
for key in globalProfileParser.options('profile'):
|
||||
if key in tempOverride:
|
||||
p.append(key + "=" + unicode(tempOverride[key]))
|
||||
tempDone.append(key)
|
||||
else:
|
||||
p.append(key + "=" + globalProfileParser.get('profile', key))
|
||||
if globalProfileParser.has_section('alterations'):
|
||||
for key in globalProfileParser.options('alterations'):
|
||||
if key in tempOverride:
|
||||
p.append(key + "=" + tempOverride[key])
|
||||
tempDone.append(key)
|
||||
else:
|
||||
alt.append(key + "=" + globalProfileParser.get('alterations', key))
|
||||
for key in tempOverride:
|
||||
if key not in tempDone:
|
||||
p.append(key + "=" + unicode(tempOverride[key]))
|
||||
ret = '\b'.join(p) + '\f' + '\b'.join(alt)
|
||||
ret = base64.b64encode(zlib.compress(ret, 9))
|
||||
return ret
|
||||
|
||||
def getProfileSetting(name):
|
||||
if name in tempOverride:
|
||||
return unicode(tempOverride[name])
|
||||
#Check if we have a configuration file loaded, else load the default.
|
||||
if not globals().has_key('globalProfileParser'):
|
||||
loadGlobalProfile(getDefaultProfilePath())
|
||||
@ -217,13 +248,27 @@ def putProfileSetting(name, value):
|
||||
globalProfileParser.add_section('profile')
|
||||
globalProfileParser.set('profile', name, str(value))
|
||||
|
||||
def isProfileSetting(name):
|
||||
if name in profileDefaultSettings:
|
||||
return True
|
||||
return False
|
||||
|
||||
## Preferences functions
|
||||
global globalPreferenceParser
|
||||
globalPreferenceParser = None
|
||||
|
||||
def getPreferencePath():
|
||||
return os.path.normpath(os.path.join(os.path.dirname(os.path.abspath(__file__)), "../preferences.ini"))
|
||||
|
||||
def getPreferenceFloat(name):
|
||||
try:
|
||||
return float(eval(getPreference(name), {}, {}))
|
||||
except (ValueError, SyntaxError):
|
||||
return 0.0
|
||||
|
||||
def getPreference(name):
|
||||
if name in tempOverride:
|
||||
return unicode(tempOverride[name])
|
||||
global globalPreferenceParser
|
||||
if globalPreferenceParser == None:
|
||||
globalPreferenceParser = ConfigParser.ConfigParser()
|
||||
@ -253,6 +298,18 @@ def putPreference(name, value):
|
||||
globalPreferenceParser.set('preference', name, unicode(value).encode("utf-8"))
|
||||
globalPreferenceParser.write(open(getPreferencePath(), 'w'))
|
||||
|
||||
def isPreference(name):
|
||||
if name in preferencesDefaultSettings:
|
||||
return True
|
||||
return False
|
||||
|
||||
## Temp overrides for multi-extruder slicing and the project planner.
|
||||
tempOverride = {}
|
||||
def setTempOverride(name, value):
|
||||
tempOverride[name] = value
|
||||
def resetTempOverride():
|
||||
tempOverride.clear()
|
||||
|
||||
#########################################################
|
||||
## Utility functions to calculate common profile values
|
||||
#########################################################
|
||||
@ -296,7 +353,11 @@ def replaceTagMatch(m):
|
||||
tag = m.group(0)[1:-1]
|
||||
if tag in ['print_speed', 'retraction_speed', 'travel_speed', 'max_z_speed', 'bottom_layer_speed', 'cool_min_feedrate']:
|
||||
return str(getProfileSettingFloat(tag) * 60)
|
||||
return str(getProfileSettingFloat(tag))
|
||||
if isProfileSetting(tag):
|
||||
return str(getProfileSettingFloat(tag))
|
||||
if isPreference(tag):
|
||||
return str(getProfileSettingFloat(tag))
|
||||
return tag
|
||||
|
||||
### Get aleration raw contents. (Used internally in Cura)
|
||||
def getAlterationFile(filename):
|
||||
@ -333,7 +394,7 @@ def getAlterationFileContents(filename):
|
||||
if filename == 'start.gcode':
|
||||
#For the start code, hack the temperature and the steps per E value into it. So the temperature is reached before the start code extrusion.
|
||||
#We also set our steps per E here, if configured.
|
||||
eSteps = float(getPreference('steps_per_e'))
|
||||
eSteps = getPreferenceFloat('steps_per_e')
|
||||
if eSteps > 0:
|
||||
prefix += 'M92 E%f\n' % (eSteps)
|
||||
temp = getProfileSettingFloat('print_temperature')
|
||||
|
@ -5,6 +5,28 @@ import platform, os, subprocess, sys
|
||||
from cura_sf.skeinforge_application.skeinforge_utilities import skeinforge_craft
|
||||
from util import profile
|
||||
|
||||
#How long does each step take compared to the others. This is used to make a better scaled progress bar, and guess time left.
|
||||
sliceStepTimeFactor = {
|
||||
'start': 3.3713991642,
|
||||
'slice': 15.4984838963,
|
||||
'preface': 5.17178297043,
|
||||
'inset': 116.362634182,
|
||||
'fill': 215.702672005,
|
||||
'multiply': 21.9536788464,
|
||||
'speed': 12.759510994,
|
||||
'raft': 31.4580039978,
|
||||
'skirt': 19.3436040878,
|
||||
'skin': 1.0,
|
||||
'joris': 1.0,
|
||||
'comb': 23.7805759907,
|
||||
'cool': 27.148763895,
|
||||
'dimension': 90.4914340973
|
||||
}
|
||||
|
||||
totalRunTimeFactor = 0
|
||||
for v in sliceStepTimeFactor.itervalues():
|
||||
totalRunTimeFactor += v
|
||||
|
||||
def getPyPyExe():
|
||||
"Return the path to the pypy executable if we can find it. Else return False"
|
||||
if platform.system() == "Windows":
|
||||
|
@ -9,11 +9,11 @@ import struct
|
||||
|
||||
from util import util3d
|
||||
|
||||
class stlFace():
|
||||
class stlFace(object):
|
||||
def __init__(self, v0, v1, v2):
|
||||
self.v = [v0, v1, v2]
|
||||
|
||||
class stlModel():
|
||||
class stlModel(object):
|
||||
def __init__(self):
|
||||
self.faces = []
|
||||
self.vertexes = []
|
||||
|
@ -1,7 +1,7 @@
|
||||
|
||||
import math
|
||||
|
||||
class Vector3():
|
||||
class Vector3(object):
|
||||
def __init__(self, x=0.0, y=0.0, z=0.0):
|
||||
self.x = x
|
||||
self.y = y
|
||||
|
Loading…
x
Reference in New Issue
Block a user