Commit f2eb8eef authored by sumpfralle's avatar sumpfralle

* re-added the startup script (before: pycamGUI)

* added almost all commandline parameters for non-interactive toolpath generation


git-svn-id: https://pycam.svn.sourceforge.net/svnroot/pycam/trunk@453 bbaffbd6-741e-11dd-a85d-61de82d9cad9
parent ec46d22b
#!/usr/bin/python
# -*- coding: utf-8 -*-
"""
$Id$
Copyright 2010 Lars Kruse <devel@sumpfralle.de>
Copyright 2008-2009 Lode Leroy
This file is part of PyCAM.
PyCAM is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
PyCAM is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with PyCAM. If not, see <http://www.gnu.org/licenses/>.
"""
from optparse import OptionParser
import sys
import os
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "src"))
from pycam.Physics.ode_physics import override_ode_availability
import pycam.Gui.common as GuiCommon
import pycam.Gui.Settings
import pycam.Importers.STLImporter
import pycam.Importers.TestModel
import pycam.Toolpath.Generator
import pycam.Toolpath
from pycam import VERSION
import pycam.Utils.log
log = pycam.Utils.log.get_logger()
EXAMPLE_MODEL_LOCATIONS = (
os.path.join(os.path.dirname(__file__), "Samples", "STL"),
"/usr/share/pycam/Samples/STL")
DEFAULT_MODEL_FILE = "pycam.stl"
def show_gui(self, inputfile=None, task_settings_file=None):
deps_gtk = GuiCommon.requirements_details_gtk()
report_gtk = GuiCommon.get_dependency_report(deps_gtk, prefix="\t")
if GuiCommon.check_dependencies(deps_gtk):
from pycam.Gui.Project import ProjectGui
gui_class = ProjectGui
else:
full_report = []
full_report.append("PyCAM dependency problem")
full_report.append("Error: Failed to load the GTK interface.")
full_report.append("Details:")
full_report.append(report_gtk)
full_report.append("")
full_report.append("Detailed list of requirements: %s" % GuiCommon.REQUIREMENTS_LINK)
log.critical(os.linesep.join(full_report))
sys.exit(1)
gui = gui_class()
gui.load_model(get_model(inputfile))
# load task settings file
if not task_settings_file is None:
gui.open_task_settings_file(task_settings_file)
# open the GUI
gui.mainloop()
def get_model(inputfile=None):
# try to load the default model file ("pycam" logo)
if inputfile is None:
for inputdir in EXAMPLE_MODEL_LOCATIONS:
inputfile = os.path.join(inputdir, DEFAULT_MODEL_FILE)
if os.path.isfile(inputfile):
model = pycam.Importers.STLImporter.ImportModel(inputfile)
break
else:
# fall back to the simple test model
log.warn("Failed to find the default model (%s) in the " \
"following locations: %s" % (DEFAULT_MODEL_FILE,
", ".join(EXAMPLE_MODEL_LOCATIONS)))
model = pycam.Importers.TestModel()
else:
model = pycam.Importers.STLImporter.ImportModel(inputfile)
return model
# check if we were started as a separate program
if __name__ == "__main__":
parser = OptionParser(
usage="usage: %prog [options] [inputfile]",
version="PyCAM v%s" % VERSION)
group_general = parser.add_option_group("General options")
group_export = parser.add_option_group("Export formats",
"Export the resulting toolpath or meta-data in various formats. " \
+ "This option triggers the non-interactive mode. Thus the GUI " \
+ "is disabled.")
group_tool = parser.add_option_group("Tool definition",
"Specify the tool paramters. The default tool is spherical and " \
+ "has a diameter of 1mm. The default speeds are 1000 (feedrate) " \
+ "and 250 (drill spindle rotations per minute)")
group_process = parser.add_option_group("Process definition",
"Specify the process parameters: toolpath strategy, layer height," \
+ " and others. A typical roughing operation is configured by " \
+ "default.")
group_bounds = parser.add_option_group("Boundary definition",
"Specify the outer limits of the processing area (x/y/z). " \
+ "You may choose between 'relative_margin' (margin is given as " \
+ "percentage of the respective model dimension), 'fixed_margin' " \
+ "(margin for each face given in absolute units of length) " \
+ "and 'custom' (absolute coordinates of the bounding box - " \
+ "regardless of the model size and position). Negative values " \
+ "are allowed and can make sense (e.g. negative margin).")
group_support_grid = parser.add_option_group("Support grid",
"An optional support grid can be used to keep the object in " \
+ "place during the mill operation. The support grid can be " \
+ "removed manually afterwards. The support grid can have a " \
+ "rectangular profile. By default the support grid is disabled.")
group_general.add_option("-c", "--config", dest="config_file",
default=None, action="store", type="string",
help="load a task settings file")
group_general.add_option("", "--unit", dest="unit_size",
default="mm", action="store", type="choice",
choices=["mm", "inch"], help="choose 'mm' or 'inch' for all " \
+ "numbers. By default 'mm' is assumed.")
group_general.add_option("", "--disable-ode", dest="ode_disabled",
default=False, action="store_true",
help="disable experimental ODE collision detection")
group_export.add_option("", "--export-gcode", dest="export_gcode",
default=None, action="store", type="string",
help="export the generated toolpaths to a file")
group_export.add_option("", "--export-task-config",
dest="export_task_config", default=None, action="store",
type="string",
help="export the current task configuration (mainly for debugging)")
group_tool.add_option("", "--tool-shape", dest="tool_shape",
default="cylindrical", action="store", type="choice",
choices=["cylindrical", "spherical", "toroidal"],
help="tool shape for the operation (cylindrical, spherical, " \
+ "toroidal)")
group_tool.add_option("", "--tool-radius", dest="tool_radius",
default=1.0, action="store", type="float",
help="radius of the tool")
group_tool.add_option("", "--tool-torus-radius", dest="tool_torus_radius",
default=0.25, action="store", type="float",
help="torus radius of the tool (only for toroidal tool shape)")
group_tool.add_option("", "--tool-feedrate", dest="tool_feedrate",
default=1000, action="store", type="float",
help="allowed movement velocity of the tool (units/minute)")
group_tool.add_option("", "--tool-speed", dest="tool_speed",
default=250, action="store", type="float",
help="rotation speed of the tool (per minute)")
group_tool.add_option("", "--tool-id", dest="tool_id",
default=1, action="store", type="int",
help="tool ID - to be used for tool selection via GCode " \
+ "(default: 1)")
group_process.add_option("", "--process-path-direction",
dest="process_path_direction", default="x", action="store",
type="choice", choices=["x", "y", "xy"],
help="primary direction of the generated toolpath (x/y/xy)")
group_process.add_option("", "--process-path-generator",
dest="process_path_generator", default="layer", action="store",
type="choice", choices=["layer", "surface", "engrave"],
help="one of the available toolpath strategies (layer, surface, " \
+ "engrave)")
group_process.add_option("", "--process-path-postprocessor",
dest="process_path_postprocessor", default="polygon",
action="store", type="choice",
choices=["polygon", "contour", "zigzag"], help="one of the " \
+ "available toolpath postprocessors (polygon, zigzag, contour)")
group_process.add_option("", "--process-material-allowance",
dest="process_material_allowance", default=0.0, action="store",
type="float", help="minimum distance between the tool and the " \
+ "object (for rough processing)")
group_process.add_option("", "--process-step-down",
dest="process_step_down", default=3.0, action="store", type="float",
help="the maximum thickness of each processed material layer " \
+ "(only for 'layer' strategy)")
group_process.add_option("", "--process-overlap-percent",
dest="process_overlap_percent", default=0, action="store",
type="int", help="how much should two adjacent parallel " \
+ "toolpaths overlap each other (0..99)")
group_process.add_option("", "--safety-height", dest="safety_height",
default=0.0, action="store", type="float",
help="height for safe re-positioning moves")
group_process.add_option("", "--process-engrave-offset",
dest="process_engrave_offset", default=0.0, action="store",
type="float", help="engrave along the contour of a model with a " \
+ "given distance (only for 'engrave' strategy)")
group_bounds.add_option("", "--bounds-type", dest="bounds_type",
default="relative-margin", action="store", type="choice",
choices=["relative-margin", "fixed-margin", "custom"],
help="type of the boundary definition (relative-margin, " \
+ "fixed-margin, custom)")
group_bounds.add_option("", "--bounds-lower", dest="bounds_lower",
default="", action="store", type="string",
help="comma-separated x/y/z combination of the lower boundary " \
+ "(e.g. '12,5.5,0')")
group_bounds.add_option("", "--bounds-upper", dest="bounds_upper",
default="", action="store", type="string",
help="comma-separated x/y/z combination of the upper boundary " \
+ "(e.g. '4,4,-0.5')")
group_support_grid.add_option("", "--enable-support-grid",
dest="support_grid_enabled", default=False, action="store_true",
help="enable the support grid")
group_support_grid.add_option("", "--support-grid-distance",
dest="support_grid_distance", default=10.0, action="store",
type="float", help="how far apart should two adjacent parallel " \
+ "lines of the support grid pattern be")
group_support_grid.add_option("", "--support-grid-height",
dest="support_grid_height", default=2.0, action="store",
type="float", help="height of the support grid profile")
group_support_grid.add_option("", "--support-grid-thickness",
dest="support_grid_thickness", default=0.5, action="store",
type="float", help="width of the support grid profile")
(opts, args) = parser.parse_args()
if len(args) > 0:
inputfile = args[0]
else:
inputfile = None
if opts.ode_disabled:
override_ode_availability(False)
if not opts.export_gcode and not opts.export_task_config:
show_gui(inputfile, opts.config_file)
else:
# generate toolpath
tps = pycam.Gui.Settings.ToolpathSettings()
tool_shape = {"cylindrical": "CylindricalCutter",
"spherical": "SphericalCutter",
"toroidal": "ToroidalCutter",
}[opts.tool_shape]
tps.set_tool(opts.tool_id, tool_shape, opts.tool_radius,
opts.tool_torus_radius, opts.tool_speed, opts.tool_feedrate)
if opts.support_grid_enabled:
tps.set_support_grid(opts.support_grid_distance,
opts.support_grid_thickness, opts.support_grid_height)
if not opts.ode_disabled:
tps.set_calculation_backend("ODE")
tps.set_unit_size(opts.unit_size)
path_generator = {"layer": "PushCutter",
"surface": "DropCutter",
"engrave": "EngraveCutter",
}[opts.process_path_generator]
postprocessor = {"zigzag": "ZigZagCutter",
"contour": "ContourCutter",
"polygon": "PolygonCutter",
}[opts.process_path_postprocessor]
tps.set_process_settings(path_generator, postprocessor,
opts.process_path_direction, opts.process_material_allowance,
opts.safety_height, opts.process_overlap_percent / 100.0,
opts.process_step_down, opts.process_engrave_offset)
model = get_model(inputfile)
# calculate the processing boundary
bounds = pycam.Toolpath.Bounds()
# set the bounds type and let the default bounding box match the model
if opts.bounds_type == "relative-margin":
bounds.set_type(pycam.Toolpath.Bounds.TYPE_RELATIVE_MARGIN)
bounds_default_low = (10, 10, 0)
bounds_default_high = (10, 10, 0)
elif opts.bounds_type == "fixed-margin":
bounds.set_type(pycam.Toolpath.Bounds.TYPE_FIXED_MARGIN)
bounds_default_low = (10, 10, 0)
bounds_default_high = (10, 10, 0)
else:
# custom boundary setting
bounds.set_type(pycam.Toolpath.Bounds.TYPE_CUSTOM)
bounds_default_low = (model.minx, model.miny, model.minz)
bounds_default_high = (model.maxx, model.maxy, model.maxz)
# TODO: use the optparse conversion callback instead
def parse_triple_float(text):
nums = text.split(",")
if len(nums) != 3:
return None
result = []
for num in nums:
try:
result.append(float(num))
except ValueError:
return None
return result
bounds_lower_nums = parse_triple_float(opts.bounds_lower)
bounds_upper_nums = parse_triple_float(opts.bounds_upper)
if bounds_lower_nums is None \
and bounds_upper_nums is None:
# no bound was given
bounds.set_bounds(bounds_default_low, bounds_default_high)
elif bounds_lower_nums is None:
# only the upper bound was specified
bounds.set_bounds(bounds_default_low, bounds_upper_nums)
elif bounds_upper_nums is None:
# only the lower bound was specified
bounds.set_bounds(bounds_lower_nums, bounds_default_high)
else:
# both lower and upper bounds were specified
bounds.set_bounds(bounds_lower_nums, bounds_upper_nums)
# TODO: inside/along/outside boundary (currently: along)
tps.set_bounds(bounds)
tp = pycam.Toolpath.Generator.generate_toolpath_from_settings(model, tps)
# write result
print tp
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment