ToolpathCrop.py 6.96 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
# -*- coding: utf-8 -*-
"""
$Id$

Copyright 2011 Lars Kruse <devel@sumpfralle.de>

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/>.
"""


import pycam.Plugins
25
from pycam.Geometry.PointUtils import *
26
from pycam.Geometry.Plane import Plane
27
import pycam.Gui.ControlsGTK
28 29 30 31 32


class ToolpathCrop(pycam.Plugins.PluginBase):

    UI_FILE = "toolpath_crop.ui"
33
    DEPENDS = ["Models", "Toolpaths"]
34
    CATEGORIES = ["Toolpath"]
35 36 37

    def setup(self):
        if self.gui:
38 39 40
            self._frame = self.gui.get_object("ToolpathCropFrame")
            self.core.register_ui("toolpath_handling", "Crop",
                    self._frame, 40)
41
            self._gtk_handlers = []
42 43 44
            for objname in ("ToolpathCropZSlice", "ToolpathCropMargin"):
                obj = self.gui.get_object(objname)
                obj.set_value(0)
45 46 47 48
                self._gtk_handlers.append((obj, "value-changed",
                        self._update_widgets))
            self._gtk_handlers.append((self.gui.get_object("CropButton"),
                    "clicked", self.crop_toolpath))
49 50
            # model selector
            self.models_widget = pycam.Gui.ControlsGTK.InputTable([],
sumpfralle's avatar
sumpfralle committed
51
                    change_handler=self._update_widgets)
52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
            # configure the input/output converter
            def get_converter(model_refs):
                models_dict = {}
                for model in self.core.get("models"):
                    models_dict[id(model)] = model
                models = []
                for model_ref in model_refs:
                    models.append(models_dict[model_ref])
                return models
            def set_converter(models):
                return [id(model) for model in models]
            self.models_widget.set_conversion(set_conv=set_converter,
                    get_conv=get_converter)
            self.gui.get_object("ModelTableContainer").add(
                    self.models_widget.get_widget())
67 68 69 70 71
            self._event_handlers = (
                    ("model-list-changed", self._update_models_list),
                    ("toolpath-selection-changed", self._update_visibility))
            self.register_gtk_handlers(self._gtk_handlers)
            self.register_event_handlers(self._event_handlers)
72 73
            self._update_widgets()
            self._update_visibility()
74 75 76 77
        return True

    def teardown(self):
        if self.gui:
78 79 80
            self.gui.get_object("ModelTableContainer").remove(
                    self.models_widget.get_widget())
            self.core.unregister_ui("toolpath_handling", self._frame)
81 82
            self.unregister_gtk_handlers(self._gtk_handlers)
            self.unregister_event_handlers(self._event_handlers)
83 84 85 86 87

    def _update_models_list(self):
        choices = []
        models = self.core.get("models")
        for model in models:
88
            choices.append((model["name"], model))
89 90 91 92 93 94 95 96 97
        self.models_widget.update_choices(choices)

    def _update_visibility(self):
        if self.core.get("toolpaths").get_selected():
            self._frame.show()
        else:
            self._frame.hide()

    def _update_widgets(self, widget=None):
98
        models = [m.model for m in self.models_widget.get_value()]
99 100 101 102 103
        info_label = self.gui.get_object("ToolpathCropInfo")
        info_box = self.gui.get_object("ToolpathCropInfoBox")
        button = self.gui.get_object("CropButton")
        slicing_models = [model for model in models
                if hasattr(model, "get_waterline_contour")]
104
        # show or hide z-slice controls
105 106 107 108 109 110 111 112 113 114 115
        slice_controls = ("ToolpathCropZSliceLabel", "ToolpathCropZSlice")
        if slicing_models:
            # set lower and upper limit for z-slice
            z_slice = self.gui.get_object("ToolpathCropZSlice")
            minz = min([model.minz for model in slicing_models])
            maxz = max([model.maxz for model in slicing_models])
            z_slice.set_range(minz, maxz)
            if z_slice.get_value() > maxz:
                z_slice.set_value(maxz)
            elif z_slice.get_value() < minz:
                z_slice.set_value(minz)
116
            else:
117 118 119 120 121
                pass
            for name in slice_controls:
                self.gui.get_object(name).show()
        else:
            for name in slice_controls:
122
                self.gui.get_object(name).hide()
123 124 125 126 127
        # update info
        if not models:
            info_box.show()
            info_label.set_label("Hint: select a model")
            button.set_sensitive(False)
128
        else:
129 130 131 132 133 134 135 136 137 138
            polygons = self._get_waterlines()
            if polygons:
                info_box.hide()
                button.set_sensitive(True)
            else:
                info_label.set_label("Hint: there is no usable contour at this splice level")
                info_box.show()
                button.set_sensitive(False)

    def _get_waterlines(self):
139
        models = [m.model for m in self.models_widget.get_value()]
140 141 142 143 144 145 146 147
        polygons = []
        # get all waterlines and polygons
        for model in models:
            if hasattr(model, "get_polygons"):
                for poly in model.get_polygons():
                    polygons.append(poly.copy())
            elif hasattr(model, "get_waterline_contour"):
                z_slice = self.gui.get_object("ToolpathCropZSlice").get_value()
148
                plane = Plane((0, 0, z_slice))
149 150 151
                for poly in model.get_waterline_contour(plane).get_polygons():
                    polygons.append(poly.copy())
        # add an offset if requested
152
        margin = self.gui.get_object("ToolpathCropMargin").get_value()
153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171
        if margin != 0:
            shifted = []
            for poly in polygons:
                shifted.extend(poly.get_offset_polygons(margin))
            polygons = shifted
        return polygons

    def crop_toolpath(self, widget=None):
        selected = self.core.get("toolpaths").get_selected()
        polygons = self._get_waterlines()
        keep_original = self.gui.get_object(
                "ToolpathCropKeepOriginal").get_active()
        for toolpath in self.core.get("toolpaths").get_selected():
            new_tp = toolpath.get_cropped_copy(polygons)
            if new_tp.paths:
                if keep_original:
                    self.core.get("toolpaths").append(new_tp)
                else:
                    toolpath.paths = new_tp.paths
172
                    self.core.emit_event("toolpath-changed")
173 174 175
            else:
                self.log.info("Toolpath cropping: the result is empty")
        self.core.get("toolpaths").select(selected)
176