Added model transformation options (scale/flip/rotate)

This commit is contained in:
daid 2012-03-07 16:58:04 +01:00
parent b09a4c14cd
commit ed61e889dc
7 changed files with 122 additions and 19 deletions

View File

@ -85,6 +85,11 @@ def getSkeinPyPyProfileInformation():
'Correct_Mesh': DEFSET,
'Unproven_Mesh': DEFSET,
'SVG_Viewer': DEFSET,
'FlipX': storedSetting("flip_x"),
'FlipY': storedSetting("flip_y"),
'FlipZ': storedSetting("flip_z"),
'Scale': storedSetting("model_scale"),
'Rotate': storedSetting("model_rotate_base"),
},'scale': {
'Activate_Scale': "False",
'XY_Plane_Scale_ratio': DEFSET,

View File

@ -26,14 +26,14 @@ class advancedConfigWindow(configBase.configWindowBase):
validators.validFloat(c, 0.0)
configBase.TitleRow(left, "Sequence")
c = configBase.SettingRow(left, "Print order sequence", 'sequence', ['Loops > Perimeter > Infill', 'Loops > Infill > Perimeter', 'Infill > Loops > Perimeter', 'Infill > Perimeter > Loops', 'Perimeter > Infill > Loops', 'Perimeter > Loops > Infill'], 'Sequence of printing. The perimeter is the outer print edge, the loops are the insides of the walls, and the infill is the insides.');
c = configBase.SettingRow(left, "Force first layer sequence", 'force_first_layer_sequence', ['True', 'False'], 'This setting forces the order of the first layer to be \'Perimeter > Loops > Infill\'')
c = configBase.SettingRow(left, "Force first layer sequence", 'force_first_layer_sequence', True, 'This setting forces the order of the first layer to be \'Perimeter > Loops > Infill\'')
configBase.TitleRow(left, "Infill")
c = configBase.SettingRow(left, "Infill pattern", 'infill_type', ['Line', 'Grid Circular', 'Grid Hexagonal', 'Grid Rectangular'], 'Pattern of the none-solid infill. Line is default, but grids can provide a strong print.')
c = configBase.SettingRow(left, "Solid infill top", 'solid_top', ['True', 'False'], 'Create a solid top surface, if set to false the top is filled with the fill percentage. Useful for cups/vases.')
c = configBase.SettingRow(left, "Solid infill top", 'solid_top', True, 'Create a solid top surface, if set to false the top is filled with the fill percentage. Useful for cups/vases.')
configBase.TitleRow(left, "Joris")
c = configBase.SettingRow(left, "Joris the outer edge", 'joris', ['False', 'True'], '[Joris] is a code name for smoothing out the Z move of the outer edge. This will create a steady Z increase over the whole print. It is intended to be used with a single walled wall thickness to make cups/vases.')
c = configBase.SettingRow(left, "Joris the outer edge", 'joris', False, '[Joris] is a code name for smoothing out the Z move of the outer edge. This will create a steady Z increase over the whole print. It is intended to be used with a single walled wall thickness to make cups/vases.')
main.Fit()
self.Fit()

View File

@ -107,20 +107,22 @@ class SettingRow():
self.type = type
self.label = wx.StaticText(panel, -1, label)
getSettingFunc = settings.getPreference
if self.type == 'profile':
if isinstance(defaultValue, types.StringTypes):
self.ctrl = wx.TextCtrl(panel, -1, settings.getProfileSetting(configName, defaultValue))
else:
self.ctrl = wx.ComboBox(panel, -1, settings.getProfileSetting(configName, defaultValue[0]), choices=defaultValue, style=wx.CB_DROPDOWN|wx.CB_READONLY)
getSettingFunc = settings.getProfileSetting
if isinstance(defaultValue, types.StringTypes):
self.ctrl = wx.TextCtrl(panel, -1, getSettingFunc(configName, defaultValue))
self.ctrl.Bind(wx.EVT_TEXT, self.OnSettingChange)
elif isinstance(defaultValue, types.BooleanType):
self.ctrl = wx.CheckBox(panel, -1, style=wx.ALIGN_RIGHT)
self.ctrl.SetValue(getSettingFunc(configName, defaultValue) == "True")
self.ctrl.Bind(wx.EVT_CHECKBOX, self.OnSettingChange)
else:
if isinstance(defaultValue, types.StringTypes):
self.ctrl = wx.TextCtrl(panel, -1, settings.getPreference(configName, defaultValue))
else:
self.ctrl = wx.ComboBox(panel, -1, settings.getPreference(configName, defaultValue[0]), choices=defaultValue, style=wx.CB_DROPDOWN|wx.CB_READONLY)
self.ctrl = wx.ComboBox(panel, -1, getSettingFunc(configName, defaultValue[0]), choices=defaultValue, style=wx.CB_DROPDOWN|wx.CB_READONLY)
self.ctrl.Bind(wx.EVT_TEXT, self.OnSettingChange)
#self.helpButton = wx.Button(panel, -1, "?", style=wx.BU_EXACTFIT)
#self.helpButton.SetToolTip(wx.ToolTip(help))
self.ctrl.Bind(wx.EVT_TEXT, self.OnSettingTextChange)
self.ctrl.Bind(wx.EVT_ENTER_WINDOW, lambda e: panel.main.OnPopupDisplay(self))
self.ctrl.Bind(wx.EVT_LEAVE_WINDOW, panel.main.OnPopupHide)
@ -131,7 +133,7 @@ class SettingRow():
#sizer.Add(helpButton, (x,y+2))
sizer.SetRows(x+1)
def OnSettingTextChange(self, e):
def OnSettingChange(self, e):
if self.type == 'profile':
settings.putProfileSetting(self.configName, self.GetValue())
else:
@ -158,10 +160,13 @@ class SettingRow():
self.panel.main.UpdatePopup(self)
def GetValue(self):
return self.ctrl.GetValue()
return str(self.ctrl.GetValue())
def SetValue(self, value):
self.ctrl.SetValue(value)
if isinstance(self.ctrl, wx.CheckBox):
self.ctrl.SetValue(str(value) == "True")
else:
self.ctrl.SetValue(value)
#Settings notify works as a validator, but instead of validating anything, it calls another function, which can use the value.
class settingNotify():
@ -176,4 +181,6 @@ class settingNotify():
self.func(f)
return validators.SUCCESS, ''
except ValueError:
self.func()
return validators.SUCCESS, ''

View File

@ -181,7 +181,7 @@ class UltimakerCheckupPage(InfoPage):
wx.MessageBox('Please move the printer head to the center of the machine\nalso move the platform so it is not at the highest or lowest position,\nand make sure the machine is powered on.', 'Machine check', wx.OK | wx.ICON_INFORMATION)
wx.CallAfter(self.AddProgressText, "Checking endstops")
if self.DoCommCommandWithTimeout('M119') != "ok x_min:l x_max:l y_min:l y_max:l z_min:l z_max:l"
if self.DoCommCommandWithTimeout('M119') != "ok x_min:l x_max:l y_min:l y_max:l z_min:l z_max:l":
wx.CallAfter(self.AddProgressText, "Error: There is a problem in your endstops!")
wx.CallAfter(self.AddProgressText, "Error: One of them seems to be pressed while it shouldn't")
return

View File

@ -147,6 +147,23 @@ class mainWindow(configBase.configWindowBase):
nb.AddPage(alterationPanel.alterationPanel(nb), "Start/End-GCode")
(left, right) = self.CreateConfigTab(nb, '3D Model')
configBase.TitleRow(left, "Scale")
c = configBase.SettingRow(left, "Scale", 'model_scale', '1.0', '')
validators.validFloat(c, 0.01)
configBase.settingNotify(c, self.preview3d.updateModelTransform)
configBase.TitleRow(left, "Flip")
c = configBase.SettingRow(left, "Flip X", 'flip_x', False, '')
configBase.settingNotify(c, self.preview3d.updateModelTransform)
c = configBase.SettingRow(left, "Flip Y", 'flip_y', False, '')
configBase.settingNotify(c, self.preview3d.updateModelTransform)
c = configBase.SettingRow(left, "Flip Z", 'flip_z', False, '')
configBase.settingNotify(c, self.preview3d.updateModelTransform)
configBase.TitleRow(right, "Rotate")
c = configBase.SettingRow(right, "Rotate (deg)", 'model_rotate_base', '0', '')
validators.validFloat(c)
configBase.settingNotify(c, self.preview3d.updateModelTransform)
# load and slice buttons.
loadButton = wx.Button(self, -1, 'Load STL')
sliceButton = wx.Button(self, -1, 'Slice to GCode')

View File

@ -110,9 +110,13 @@ class previewPanel(wx.Panel):
def DoModelLoad(self):
self.modelDirty = False
self.triangleMesh = fabmetheus_interpret.getCarving(self.modelFilename)
triangleMesh = fabmetheus_interpret.getCarving(self.modelFilename)
triangleMesh.origonalVertexes = list(triangleMesh.vertexes)
for i in xrange(0, len(triangleMesh.origonalVertexes)):
triangleMesh.origonalVertexes[i] = triangleMesh.origonalVertexes[i].copy()
self.triangleMesh = triangleMesh
self.gcode = None
self.moveModel()
self.updateModelTransform()
wx.CallAfter(self.updateToolbar)
wx.CallAfter(self.glCanvas.Refresh)
@ -145,6 +149,38 @@ class previewPanel(wx.Panel):
self.glCanvas.renderTransparent = False
self.glCanvas.Refresh()
def updateModelTransform(self, f=0):
if self.triangleMesh == None:
return
for face in self.triangleMesh.faces:
face.normal = None
scale = 1.0
rotate = 0.0
try:
scale = float(settings.getProfileSetting('model_scale', '1.0'))
rotate = float(settings.getProfileSetting('model_rotate_base', '0.0')) / 180 * math.pi
except:
pass
scaleX = scale
scaleY = scale
scaleZ = scale
if settings.getProfileSetting('flip_x') == 'True':
scaleX = -scaleX
if settings.getProfileSetting('flip_y') == 'True':
scaleY = -scaleY
if settings.getProfileSetting('flip_z') == 'True':
scaleZ = -scaleZ
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.triangleMesh.origonalVertexes)):
self.triangleMesh.vertexes[i].x = self.triangleMesh.origonalVertexes[i].x * mat00 + self.triangleMesh.origonalVertexes[i].y * mat01
self.triangleMesh.vertexes[i].y = self.triangleMesh.origonalVertexes[i].x * mat10 + self.triangleMesh.origonalVertexes[i].y * mat11
self.triangleMesh.vertexes[i].z = self.triangleMesh.origonalVertexes[i].z * scaleZ
self.moveModel()
def moveModel(self):
if self.triangleMesh == None:
return
@ -159,6 +195,7 @@ class previewPanel(wx.Panel):
v.y += self.machineCenter.y
self.triangleMesh.getMinimumZ()
self.modelDirty = True
self.glCanvas.Refresh()
class PreviewGLCanvas(glcanvas.GLCanvas):
def __init__(self, parent):
@ -343,13 +380,17 @@ class PreviewGLCanvas(glcanvas.GLCanvas):
v1 = self.parent.triangleMesh.vertexes[face.vertexIndexes[0]]
v2 = self.parent.triangleMesh.vertexes[face.vertexIndexes[1]]
v3 = self.parent.triangleMesh.vertexes[face.vertexIndexes[2]]
if not hasattr(face, 'normal'):
if face.normal == None:
face.normal = (v2 - v1).cross(v3 - v1)
face.normal.normalize()
glNormal3f(face.normal.x, face.normal.y, face.normal.z)
glVertex3f(v1.x, v1.y, v1.z)
glVertex3f(v2.x, v2.y, v2.z)
glVertex3f(v3.x, v3.y, v3.z)
glNormal3f(-face.normal.x, -face.normal.y, -face.normal.z)
glVertex3f(v1.x, v1.y, v1.z)
glVertex3f(v3.x, v3.y, v3.z)
glVertex3f(v2.x, v2.y, v2.z)
glEnd()
glEndList()
if self.renderTransparent:
@ -432,6 +473,7 @@ class PreviewGLCanvas(glcanvas.GLCanvas):
glEnable(GL_LIGHTING)
glEnable(GL_LIGHT0)
glEnable(GL_DEPTH_TEST)
glEnable(GL_CULL_FACE)
glDisable(GL_BLEND)
glClearColor(0.0, 0.0, 0.0, 1.0)

View File

@ -107,10 +107,12 @@ from fabmetheus_utilities import settings
from fabmetheus_utilities import svg_writer
from skeinforge_application.skeinforge_utilities import skeinforge_polyfile
from skeinforge_application.skeinforge_utilities import skeinforge_profile
from fabmetheus_utilities.vector3 import Vector3
import math
import os
import sys
import time
import math
__author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
@ -178,6 +180,13 @@ class CarveRepository:
settings.LabelSeparator().getFromRepository(self)
self.executeTitle = 'Carve'
self.flipX = settings.BooleanSetting().getFromValue('FlipX', self, False)
self.flipY = settings.BooleanSetting().getFromValue('FlipY', self, False)
self.flipZ = settings.BooleanSetting().getFromValue('FlipZ', self, False)
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 )
def execute(self):
"Carve button has been clicked."
fileNames = skeinforge_polyfile.getFileOrDirectoryTypes(self.fileNameInput.value, fabmetheus_interpret.getImportPluginFileNames(), self.fileNameInput.wasCancelled)
@ -189,6 +198,29 @@ class CarveSkein:
"A class to carve a carving."
def getCarvedSVG(self, carving, fileName, repository):
"Parse gnu triangulated surface text and store the carved gcode."
scale = repository.scale.value
rotate = repository.rotate.value / 180 * math.pi
scaleX = scale
scaleY = scale
scaleZ = scale
if repository.flipX.value == 'True':
scaleX = -scaleX
if repository.flipY.value == 'True':
scaleY = -scaleY
if repository.flipZ.value == 'True':
scaleZ = -scaleZ
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(carving.vertexes)):
carving.vertexes[i] = Vector3(
carving.vertexes[i].x * mat00 + carving.vertexes[i].y * mat01,
carving.vertexes[i].x * mat10 + carving.vertexes[i].y * mat11,
carving.vertexes[i].z * scaleZ)
layerHeight = repository.layerHeight.value
edgeWidth = repository.edgeWidth.value
carving.setCarveLayerHeight(layerHeight)