Merged Beta_8.994 into AutolevellingFeature
This commit is contained in:
commit
2bbab19e0a
50
CHANGELOG.md
50
CHANGELOG.md
|
@ -7,6 +7,56 @@ CHANGELOG for FlatCAM beta
|
|||
|
||||
=================================================
|
||||
|
||||
21.10.2020
|
||||
|
||||
- in Geometry Object fixed the issue with not using the End X-Y value and also made some other updates here
|
||||
- in NCC and Paint Tool fixed some issues with missing keys in the tool data dictionary
|
||||
- In Excellon Object UI fixed the enable/disable for the Milling section according to the Tools Table row that is selected
|
||||
- In Excellon Object UI fixed the milling geometry generation
|
||||
- updated the translations strings to the changes in the source code
|
||||
- some strings changed
|
||||
- fixed crash on using shortcut for creating a new Document Object
|
||||
- fixed Cutout Tool to work with the endxy parameter
|
||||
- added the exclusion parameters for Drilling Tool to the Preferences area
|
||||
- cascaded_union() method will be deprecated in Shapely 1.8 in favor of unary_union; replaced the usage of cascaded_union with unary_union in all the app
|
||||
- added some strings to the translatable strings and updated the translation strings
|
||||
|
||||
20.10.2020
|
||||
|
||||
- finished to add the Properties data to the Object Properties (former Selected Tab)
|
||||
|
||||
19.10.2020
|
||||
|
||||
- added a check (and added to Preferences too) for the verification of tools validity in the Isolation Tool
|
||||
- fixed QrCode Tool
|
||||
- updated the Turkish translation (by Mehmet Kaya)
|
||||
|
||||
18.10.2020
|
||||
|
||||
- fixed issue with calling the inform signal in the FlatCAMDefaults.load method
|
||||
- fixed macro parsing in Gerber files generated by KiCAD 4.99 (KiCAD 5.0)
|
||||
|
||||
17.10.2020
|
||||
|
||||
- updated Turkish translation (by Mehmet Kaya)
|
||||
|
||||
8.10.2020
|
||||
|
||||
- small change in the NCC Tool UI
|
||||
- some strings are changed and therefore the translation strings source are updated
|
||||
- Isolation Tool - added a check for having a complete isolation
|
||||
|
||||
7.10.2020
|
||||
|
||||
- working on adding DPI setting for PNG export in Film Tool - update
|
||||
- finished updating DPI setting feature for PNG export in Film Tool
|
||||
|
||||
5.10.2020
|
||||
|
||||
- working on adding DPI setting for PNG export in the Film Tool
|
||||
- finished working in adding DPI settings for PNG export in Film Tool although there are some limitations due of Reportlab
|
||||
- small change in TclCommandExportSVG
|
||||
|
||||
26.09.2020
|
||||
|
||||
- the Selected Tab is now Properties Tab for FlatCAM objects
|
||||
|
|
|
@ -16,12 +16,12 @@ from PyQt5.QtCore import Qt, QSettings
|
|||
|
||||
from camlib import distance, arc, three_point_circle, Geometry, FlatCAMRTreeStorage
|
||||
from appTool import AppTool
|
||||
from appGUI.GUIElements import OptionalInputSection, FCCheckBox, FCEntry, FCComboBox, FCTextAreaRich, \
|
||||
from appGUI.GUIElements import OptionalInputSection, FCCheckBox, FCLabel, FCComboBox, FCTextAreaRich, \
|
||||
FCDoubleSpinner, FCButton, FCInputDialog, FCTree, NumericalEvalTupleEntry
|
||||
from appParsers.ParseFont import *
|
||||
|
||||
from shapely.geometry import LineString, LinearRing, MultiLineString, Polygon, MultiPolygon
|
||||
from shapely.ops import cascaded_union, unary_union, linemerge
|
||||
from shapely.ops import unary_union, linemerge
|
||||
import shapely.affinity as affinity
|
||||
from shapely.geometry.polygon import orient
|
||||
|
||||
|
@ -46,7 +46,7 @@ class BufferSelectionTool(AppTool):
|
|||
Simple input for buffer distance.
|
||||
"""
|
||||
|
||||
toolName = "Buffer Selection"
|
||||
toolName = _("Buffer Selection")
|
||||
|
||||
def __init__(self, app, draw_app):
|
||||
AppTool.__init__(self, app)
|
||||
|
@ -55,7 +55,7 @@ class BufferSelectionTool(AppTool):
|
|||
self.decimals = app.decimals
|
||||
|
||||
# Title
|
||||
title_label = QtWidgets.QLabel("%s" % ('Editor ' + self.toolName))
|
||||
title_label = FCLabel("%s" % ('Editor ' + self.toolName))
|
||||
title_label.setStyleSheet("""
|
||||
QLabel
|
||||
{
|
||||
|
@ -82,7 +82,7 @@ class BufferSelectionTool(AppTool):
|
|||
self.buffer_distance_entry.set_precision(self.decimals)
|
||||
self.buffer_distance_entry.set_range(0.0000, 999999.9999)
|
||||
form_layout.addRow(_("Buffer distance:"), self.buffer_distance_entry)
|
||||
self.buffer_corner_lbl = QtWidgets.QLabel(_("Buffer corner:"))
|
||||
self.buffer_corner_lbl = FCLabel(_("Buffer corner:"))
|
||||
self.buffer_corner_lbl.setToolTip(
|
||||
_("There are 3 types of corners:\n"
|
||||
" - 'Round': the corner is rounded for exterior buffer.\n"
|
||||
|
@ -99,15 +99,15 @@ class BufferSelectionTool(AppTool):
|
|||
hlay = QtWidgets.QHBoxLayout()
|
||||
self.buffer_tools_box.addLayout(hlay)
|
||||
|
||||
self.buffer_int_button = QtWidgets.QPushButton(_("Buffer Interior"))
|
||||
self.buffer_int_button = FCButton(_("Buffer Interior"))
|
||||
hlay.addWidget(self.buffer_int_button)
|
||||
self.buffer_ext_button = QtWidgets.QPushButton(_("Buffer Exterior"))
|
||||
self.buffer_ext_button = FCButton(_("Buffer Exterior"))
|
||||
hlay.addWidget(self.buffer_ext_button)
|
||||
|
||||
hlay1 = QtWidgets.QHBoxLayout()
|
||||
self.buffer_tools_box.addLayout(hlay1)
|
||||
|
||||
self.buffer_button = QtWidgets.QPushButton(_("Full Buffer"))
|
||||
self.buffer_button = FCButton(_("Full Buffer"))
|
||||
hlay1.addWidget(self.buffer_button)
|
||||
|
||||
self.layout.addStretch()
|
||||
|
@ -191,7 +191,7 @@ class TextInputTool(AppTool):
|
|||
Simple input for buffer distance.
|
||||
"""
|
||||
|
||||
toolName = "Text Input Tool"
|
||||
toolName = _("Text Input Tool")
|
||||
|
||||
def __init__(self, app):
|
||||
AppTool.__init__(self, app)
|
||||
|
@ -212,7 +212,7 @@ class TextInputTool(AppTool):
|
|||
self.text_tool_frame.setLayout(self.text_tools_box)
|
||||
|
||||
# Title
|
||||
title_label = QtWidgets.QLabel("%s" % ('Editor ' + self.toolName))
|
||||
title_label = FCLabel("%s" % self.toolName)
|
||||
title_label.setStyleSheet("""
|
||||
QLabel
|
||||
{
|
||||
|
@ -238,7 +238,7 @@ class TextInputTool(AppTool):
|
|||
|
||||
self.font_type_cb = QtWidgets.QFontComboBox(self)
|
||||
self.font_type_cb.setCurrentFont(f_current)
|
||||
self.form_layout.addRow(QtWidgets.QLabel('%s:' % _("Font")), self.font_type_cb)
|
||||
self.form_layout.addRow(FCLabel('%s:' % _("Font")), self.font_type_cb)
|
||||
|
||||
# Flag variables to show if font is bold, italic, both or none (regular)
|
||||
self.font_bold = False
|
||||
|
@ -310,7 +310,7 @@ class TextInputTool(AppTool):
|
|||
self.font_italic_tb.setIcon(QtGui.QIcon(self.app.resource_location + '/italic32.png'))
|
||||
hlay.addWidget(self.font_italic_tb)
|
||||
|
||||
self.form_layout.addRow(QtWidgets.QLabel('%s:' % "Size"), hlay)
|
||||
self.form_layout.addRow(FCLabel('%s:' % "Size"), hlay)
|
||||
|
||||
# Text input
|
||||
self.text_input_entry = FCTextAreaRich()
|
||||
|
@ -319,13 +319,13 @@ class TextInputTool(AppTool):
|
|||
# self.text_input_entry.setMaximumHeight(150)
|
||||
self.text_input_entry.setCurrentFont(f_current)
|
||||
self.text_input_entry.setFontPointSize(10)
|
||||
self.form_layout.addRow(QtWidgets.QLabel('%s:' % _("Text")), self.text_input_entry)
|
||||
self.form_layout.addRow(FCLabel('%s:' % _("Text")), self.text_input_entry)
|
||||
|
||||
# Buttons
|
||||
hlay1 = QtWidgets.QHBoxLayout()
|
||||
self.form_layout.addRow("", hlay1)
|
||||
hlay1.addStretch()
|
||||
self.apply_button = QtWidgets.QPushButton("Apply")
|
||||
self.apply_button = FCButton(_("Apply"))
|
||||
hlay1.addWidget(self.apply_button)
|
||||
|
||||
# self.layout.addStretch()
|
||||
|
@ -409,7 +409,7 @@ class PaintOptionsTool(AppTool):
|
|||
Inputs to specify how to paint the selected polygons.
|
||||
"""
|
||||
|
||||
toolName = "Paint Tool"
|
||||
toolName = _("Paint Tool")
|
||||
|
||||
def __init__(self, app, fcdraw):
|
||||
AppTool.__init__(self, app)
|
||||
|
@ -419,7 +419,7 @@ class PaintOptionsTool(AppTool):
|
|||
self.decimals = self.app.decimals
|
||||
|
||||
# Title
|
||||
title_label = QtWidgets.QLabel("%s" % ('Editor ' + self.toolName))
|
||||
title_label = FCLabel("%s" % self.toolName)
|
||||
title_label.setStyleSheet("""
|
||||
QLabel
|
||||
{
|
||||
|
@ -435,7 +435,7 @@ class PaintOptionsTool(AppTool):
|
|||
grid.setColumnStretch(1, 1)
|
||||
|
||||
# Tool dia
|
||||
ptdlabel = QtWidgets.QLabel('%s:' % _('Tool dia'))
|
||||
ptdlabel = FCLabel('%s:' % _('Tool dia'))
|
||||
ptdlabel.setToolTip(
|
||||
_("Diameter of the tool to be used in the operation.")
|
||||
)
|
||||
|
@ -447,7 +447,7 @@ class PaintOptionsTool(AppTool):
|
|||
grid.addWidget(self.painttooldia_entry, 0, 1)
|
||||
|
||||
# Overlap
|
||||
ovlabel = QtWidgets.QLabel('%s:' % _('Overlap'))
|
||||
ovlabel = FCLabel('%s:' % _('Overlap'))
|
||||
ovlabel.setToolTip(
|
||||
_("How much (percentage) of the tool width to overlap each tool pass.\n"
|
||||
"Adjust the value starting with lower values\n"
|
||||
|
@ -467,7 +467,7 @@ class PaintOptionsTool(AppTool):
|
|||
grid.addWidget(self.paintoverlap_entry, 1, 1)
|
||||
|
||||
# Margin
|
||||
marginlabel = QtWidgets.QLabel('%s:' % _('Margin'))
|
||||
marginlabel = FCLabel('%s:' % _('Margin'))
|
||||
marginlabel.setToolTip(
|
||||
_("Distance by which to avoid\n"
|
||||
"the edges of the polygon to\n"
|
||||
|
@ -481,7 +481,7 @@ class PaintOptionsTool(AppTool):
|
|||
grid.addWidget(self.paintmargin_entry, 2, 1)
|
||||
|
||||
# Method
|
||||
methodlabel = QtWidgets.QLabel('%s:' % _('Method'))
|
||||
methodlabel = FCLabel('%s:' % _('Method'))
|
||||
methodlabel.setToolTip(
|
||||
_("Algorithm to paint the polygons:\n"
|
||||
"- Standard: Fixed step inwards.\n"
|
||||
|
@ -502,7 +502,7 @@ class PaintOptionsTool(AppTool):
|
|||
grid.addWidget(self.paintmethod_combo, 3, 1)
|
||||
|
||||
# Connect lines
|
||||
pathconnectlabel = QtWidgets.QLabel(_("Connect:"))
|
||||
pathconnectlabel = FCLabel('%s:' % _("Connect"))
|
||||
pathconnectlabel.setToolTip(
|
||||
_("Draw lines between resulting\n"
|
||||
"segments to minimize tool lifts.")
|
||||
|
@ -512,7 +512,7 @@ class PaintOptionsTool(AppTool):
|
|||
grid.addWidget(pathconnectlabel, 4, 0)
|
||||
grid.addWidget(self.pathconnect_cb, 4, 1)
|
||||
|
||||
contourlabel = QtWidgets.QLabel(_("Contour:"))
|
||||
contourlabel = FCLabel('%s:' % _("Contour"))
|
||||
contourlabel.setToolTip(
|
||||
_("Cut around the perimeter of the polygon\n"
|
||||
"to trim rough edges.")
|
||||
|
@ -525,7 +525,7 @@ class PaintOptionsTool(AppTool):
|
|||
# Buttons
|
||||
hlay = QtWidgets.QHBoxLayout()
|
||||
self.layout.addLayout(hlay)
|
||||
self.paint_button = QtWidgets.QPushButton(_("Paint"))
|
||||
self.paint_button = FCButton(_("Paint"))
|
||||
hlay.addWidget(self.paint_button)
|
||||
|
||||
self.layout.addStretch()
|
||||
|
@ -619,7 +619,7 @@ class TransformEditorTool(AppTool):
|
|||
self.decimals = self.app.decimals
|
||||
|
||||
# ## Title
|
||||
title_label = QtWidgets.QLabel("%s" % self.toolName)
|
||||
title_label = FCLabel("%s" % self.toolName)
|
||||
title_label.setStyleSheet("""
|
||||
QLabel
|
||||
{
|
||||
|
@ -628,7 +628,7 @@ class TransformEditorTool(AppTool):
|
|||
}
|
||||
""")
|
||||
self.layout.addWidget(title_label)
|
||||
self.layout.addWidget(QtWidgets.QLabel(''))
|
||||
self.layout.addWidget(FCLabel(''))
|
||||
|
||||
# ## Layout
|
||||
grid0 = QtWidgets.QGridLayout()
|
||||
|
@ -637,10 +637,10 @@ class TransformEditorTool(AppTool):
|
|||
grid0.setColumnStretch(1, 1)
|
||||
grid0.setColumnStretch(2, 0)
|
||||
|
||||
grid0.addWidget(QtWidgets.QLabel(''))
|
||||
grid0.addWidget(FCLabel(''))
|
||||
|
||||
# Reference
|
||||
ref_label = QtWidgets.QLabel('%s:' % _("Reference"))
|
||||
ref_label = FCLabel('%s:' % _("Reference"))
|
||||
ref_label.setToolTip(
|
||||
_("The reference point for Rotate, Skew, Scale, Mirror.\n"
|
||||
"Can be:\n"
|
||||
|
@ -656,7 +656,7 @@ class TransformEditorTool(AppTool):
|
|||
grid0.addWidget(ref_label, 0, 0)
|
||||
grid0.addWidget(self.ref_combo, 0, 1, 1, 2)
|
||||
|
||||
self.point_label = QtWidgets.QLabel('%s:' % _("Value"))
|
||||
self.point_label = FCLabel('%s:' % _("Value"))
|
||||
self.point_label.setToolTip(
|
||||
_("A point of reference in format X,Y.")
|
||||
)
|
||||
|
@ -677,10 +677,10 @@ class TransformEditorTool(AppTool):
|
|||
grid0.addWidget(separator_line, 5, 0, 1, 3)
|
||||
|
||||
# ## Rotate Title
|
||||
rotate_title_label = QtWidgets.QLabel("<font size=3><b>%s</b></font>" % self.rotateName)
|
||||
rotate_title_label = FCLabel("<font size=3><b>%s</b></font>" % self.rotateName)
|
||||
grid0.addWidget(rotate_title_label, 6, 0, 1, 3)
|
||||
|
||||
self.rotate_label = QtWidgets.QLabel('%s:' % _("Angle"))
|
||||
self.rotate_label = FCLabel('%s:' % _("Angle"))
|
||||
self.rotate_label.setToolTip(
|
||||
_("Angle for Rotation action, in degrees.\n"
|
||||
"Float number between -360 and 359.\n"
|
||||
|
@ -714,7 +714,7 @@ class TransformEditorTool(AppTool):
|
|||
grid0.addWidget(separator_line, 8, 0, 1, 3)
|
||||
|
||||
# ## Skew Title
|
||||
skew_title_label = QtWidgets.QLabel("<font size=3><b>%s</b></font>" % self.skewName)
|
||||
skew_title_label = FCLabel("<font size=3><b>%s</b></font>" % self.skewName)
|
||||
grid0.addWidget(skew_title_label, 9, 0, 1, 2)
|
||||
|
||||
self.skew_link_cb = FCCheckBox()
|
||||
|
@ -725,7 +725,7 @@ class TransformEditorTool(AppTool):
|
|||
|
||||
grid0.addWidget(self.skew_link_cb, 9, 2)
|
||||
|
||||
self.skewx_label = QtWidgets.QLabel('%s:' % _("X angle"))
|
||||
self.skewx_label = FCLabel('%s:' % _("X angle"))
|
||||
self.skewx_label.setToolTip(
|
||||
_("Angle for Skew action, in degrees.\n"
|
||||
"Float number between -360 and 360.")
|
||||
|
@ -746,7 +746,7 @@ class TransformEditorTool(AppTool):
|
|||
grid0.addWidget(self.skewx_entry, 10, 1)
|
||||
grid0.addWidget(self.skewx_button, 10, 2)
|
||||
|
||||
self.skewy_label = QtWidgets.QLabel('%s:' % _("Y angle"))
|
||||
self.skewy_label = FCLabel('%s:' % _("Y angle"))
|
||||
self.skewy_label.setToolTip(
|
||||
_("Angle for Skew action, in degrees.\n"
|
||||
"Float number between -360 and 360.")
|
||||
|
@ -776,7 +776,7 @@ class TransformEditorTool(AppTool):
|
|||
grid0.addWidget(separator_line, 14, 0, 1, 3)
|
||||
|
||||
# ## Scale Title
|
||||
scale_title_label = QtWidgets.QLabel("<font size=3><b>%s</b></font>" % self.scaleName)
|
||||
scale_title_label = FCLabel("<font size=3><b>%s</b></font>" % self.scaleName)
|
||||
grid0.addWidget(scale_title_label, 15, 0, 1, 2)
|
||||
|
||||
self.scale_link_cb = FCCheckBox()
|
||||
|
@ -787,7 +787,7 @@ class TransformEditorTool(AppTool):
|
|||
|
||||
grid0.addWidget(self.scale_link_cb, 15, 2)
|
||||
|
||||
self.scalex_label = QtWidgets.QLabel('%s:' % _("X factor"))
|
||||
self.scalex_label = FCLabel('%s:' % _("X factor"))
|
||||
self.scalex_label.setToolTip(
|
||||
_("Factor for scaling on X axis.")
|
||||
)
|
||||
|
@ -807,7 +807,7 @@ class TransformEditorTool(AppTool):
|
|||
grid0.addWidget(self.scalex_entry, 17, 1)
|
||||
grid0.addWidget(self.scalex_button, 17, 2)
|
||||
|
||||
self.scaley_label = QtWidgets.QLabel('%s:' % _("Y factor"))
|
||||
self.scaley_label = FCLabel('%s:' % _("Y factor"))
|
||||
self.scaley_label.setToolTip(
|
||||
_("Factor for scaling on Y axis.")
|
||||
)
|
||||
|
@ -840,7 +840,7 @@ class TransformEditorTool(AppTool):
|
|||
grid0.addWidget(separator_line, 21, 0, 1, 3)
|
||||
|
||||
# ## Flip Title
|
||||
flip_title_label = QtWidgets.QLabel("<font size=3><b>%s</b></font>" % self.flipName)
|
||||
flip_title_label = FCLabel("<font size=3><b>%s</b></font>" % self.flipName)
|
||||
grid0.addWidget(flip_title_label, 23, 0, 1, 3)
|
||||
|
||||
self.flipx_button = FCButton(_("Flip on X"))
|
||||
|
@ -865,10 +865,10 @@ class TransformEditorTool(AppTool):
|
|||
grid0.addWidget(separator_line, 27, 0, 1, 3)
|
||||
|
||||
# ## Offset Title
|
||||
offset_title_label = QtWidgets.QLabel("<font size=3><b>%s</b></font>" % self.offsetName)
|
||||
offset_title_label = FCLabel("<font size=3><b>%s</b></font>" % self.offsetName)
|
||||
grid0.addWidget(offset_title_label, 29, 0, 1, 3)
|
||||
|
||||
self.offx_label = QtWidgets.QLabel('%s:' % _("X val"))
|
||||
self.offx_label = FCLabel('%s:' % _("X val"))
|
||||
self.offx_label.setToolTip(
|
||||
_("Distance to offset on X axis. In current units.")
|
||||
)
|
||||
|
@ -888,7 +888,7 @@ class TransformEditorTool(AppTool):
|
|||
grid0.addWidget(self.offx_entry, 31, 1)
|
||||
grid0.addWidget(self.offx_button, 31, 2)
|
||||
|
||||
self.offy_label = QtWidgets.QLabel('%s:' % _("Y val"))
|
||||
self.offy_label = FCLabel('%s:' % _("Y val"))
|
||||
self.offy_label.setToolTip(
|
||||
_("Distance to offset on Y axis. In current units.")
|
||||
)
|
||||
|
@ -914,7 +914,7 @@ class TransformEditorTool(AppTool):
|
|||
grid0.addWidget(separator_line, 34, 0, 1, 3)
|
||||
|
||||
# ## Buffer Title
|
||||
buffer_title_label = QtWidgets.QLabel("<font size=3><b>%s</b></font>" % self.bufferName)
|
||||
buffer_title_label = FCLabel("<font size=3><b>%s</b></font>" % self.bufferName)
|
||||
grid0.addWidget(buffer_title_label, 35, 0, 1, 2)
|
||||
|
||||
self.buffer_rounded_cb = FCCheckBox('%s' % _("Rounded"))
|
||||
|
@ -927,7 +927,7 @@ class TransformEditorTool(AppTool):
|
|||
|
||||
grid0.addWidget(self.buffer_rounded_cb, 35, 2)
|
||||
|
||||
self.buffer_label = QtWidgets.QLabel('%s:' % _("Distance"))
|
||||
self.buffer_label = FCLabel('%s:' % _("Distance"))
|
||||
self.buffer_label.setToolTip(
|
||||
_("A positive value will create the effect of dilation,\n"
|
||||
"while a negative value will create the effect of erosion.\n"
|
||||
|
@ -952,7 +952,7 @@ class TransformEditorTool(AppTool):
|
|||
grid0.addWidget(self.buffer_entry, 37, 1)
|
||||
grid0.addWidget(self.buffer_button, 37, 2)
|
||||
|
||||
self.buffer_factor_label = QtWidgets.QLabel('%s:' % _("Value"))
|
||||
self.buffer_factor_label = FCLabel('%s:' % _("Value"))
|
||||
self.buffer_factor_label.setToolTip(
|
||||
_("A positive value will create the effect of dilation,\n"
|
||||
"while a negative value will create the effect of erosion.\n"
|
||||
|
@ -978,7 +978,7 @@ class TransformEditorTool(AppTool):
|
|||
grid0.addWidget(self.buffer_factor_entry, 38, 1)
|
||||
grid0.addWidget(self.buffer_factor_button, 38, 2)
|
||||
|
||||
grid0.addWidget(QtWidgets.QLabel(''), 42, 0, 1, 3)
|
||||
grid0.addWidget(FCLabel(''), 42, 0, 1, 3)
|
||||
|
||||
self.layout.addStretch()
|
||||
|
||||
|
@ -3147,7 +3147,7 @@ class FCEraser(FCShapeTool):
|
|||
temp_shape = eraser_shape.buffer(0.0000001)
|
||||
temp_shape = Polygon(temp_shape.exterior)
|
||||
eraser_sel_shapes.append(temp_shape)
|
||||
eraser_sel_shapes = cascaded_union(eraser_sel_shapes)
|
||||
eraser_sel_shapes = unary_union(eraser_sel_shapes)
|
||||
|
||||
for obj_shape in self.storage.get_objects():
|
||||
try:
|
||||
|
@ -3273,15 +3273,15 @@ class AppGeoEditor(QtCore.QObject):
|
|||
|
||||
# ## Page Title icon
|
||||
pixmap = QtGui.QPixmap(self.app.resource_location + '/flatcam_icon32.png')
|
||||
self.icon = QtWidgets.QLabel()
|
||||
self.icon = FCLabel()
|
||||
self.icon.setPixmap(pixmap)
|
||||
self.title_box.addWidget(self.icon, stretch=0)
|
||||
|
||||
# ## Title label
|
||||
self.title_label = QtWidgets.QLabel("<font size=5><b>%s</b></font>" % _('Geometry Editor'))
|
||||
self.title_label = FCLabel("<font size=5><b>%s</b></font>" % _('Geometry Editor'))
|
||||
self.title_label.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
|
||||
self.title_box.addWidget(self.title_label, stretch=1)
|
||||
self.title_box.addWidget(QtWidgets.QLabel(''))
|
||||
self.title_box.addWidget(FCLabel(''))
|
||||
|
||||
self.tw = FCTree(columns=3, header_hidden=False, protected_column=[0, 1], extended_sel=True)
|
||||
self.tw.setHeaderLabels(["ID", _("Type"), _("Name")])
|
||||
|
@ -3298,7 +3298,7 @@ class AppGeoEditor(QtCore.QObject):
|
|||
layout.addStretch()
|
||||
|
||||
# Editor
|
||||
self.exit_editor_button = QtWidgets.QPushButton(_('Exit Editor'))
|
||||
self.exit_editor_button = FCButton(_('Exit Editor'))
|
||||
self.exit_editor_button.setIcon(QtGui.QIcon(self.app.resource_location + '/power16.png'))
|
||||
self.exit_editor_button.setToolTip(
|
||||
_("Exit from Editor.")
|
||||
|
@ -5134,7 +5134,7 @@ class AppGeoEditor(QtCore.QObject):
|
|||
return
|
||||
|
||||
# add the result to the results list
|
||||
results.append(cascaded_union(local_results))
|
||||
results.append(unary_union(local_results))
|
||||
|
||||
# This is a dirty patch:
|
||||
for r in results:
|
||||
|
|
|
@ -9,7 +9,7 @@ from PyQt5 import QtGui, QtCore, QtWidgets
|
|||
from PyQt5.QtCore import Qt, QSettings
|
||||
|
||||
from shapely.geometry import LineString, LinearRing, MultiLineString, Point, Polygon, MultiPolygon, box
|
||||
from shapely.ops import cascaded_union
|
||||
from shapely.ops import unary_union
|
||||
import shapely.affinity as affinity
|
||||
|
||||
from vispy.geometry import Rect
|
||||
|
@ -2235,7 +2235,7 @@ class FCEraser(FCShapeTool):
|
|||
temp_shape = eraser_shape['solid'].buffer(0.0000001)
|
||||
temp_shape = Polygon(temp_shape.exterior)
|
||||
eraser_sel_shapes.append(temp_shape)
|
||||
eraser_sel_shapes = cascaded_union(eraser_sel_shapes)
|
||||
eraser_sel_shapes = unary_union(eraser_sel_shapes)
|
||||
|
||||
for storage in self.draw_app.storage_dict:
|
||||
try:
|
||||
|
@ -4968,7 +4968,7 @@ class AppGerberEditor(QtCore.QObject):
|
|||
if 'solid' in actual_geo:
|
||||
edit_geo.append(actual_geo['solid'])
|
||||
|
||||
all_geo = cascaded_union(edit_geo)
|
||||
all_geo = unary_union(edit_geo)
|
||||
|
||||
# calculate the bounds values for the edited Gerber object
|
||||
xmin, ymin, xmax, ymax = all_geo.bounds
|
||||
|
|
|
@ -2684,9 +2684,9 @@ class MainGUI(QtWidgets.QMainWindow):
|
|||
if key == QtCore.Qt.Key_B:
|
||||
self.app.app_obj.new_gerber_object()
|
||||
|
||||
# New Geometry
|
||||
# New Document Object
|
||||
if key == QtCore.Qt.Key_D:
|
||||
self.app.new_document_object()
|
||||
self.app.app_obj.new_document_object()
|
||||
|
||||
# Copy Object Name
|
||||
if key == QtCore.Qt.Key_E:
|
||||
|
|
|
@ -251,10 +251,31 @@ class GerberObjectUI(ObjectUI):
|
|||
""")
|
||||
grid0.addWidget(self.editor_button, 4, 0, 1, 3)
|
||||
|
||||
separator_line = QtWidgets.QFrame()
|
||||
separator_line.setFrameShape(QtWidgets.QFrame.HLine)
|
||||
separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
|
||||
grid0.addWidget(separator_line, 6, 0, 1, 3)
|
||||
# PROPERTIES CB
|
||||
self.properties_button = FCButton('%s' % _("PROPERTIES"), checkable=True)
|
||||
self.properties_button.setIcon(QtGui.QIcon(self.app.resource_location + '/properties32.png'))
|
||||
self.properties_button.setToolTip(_("Show the Properties."))
|
||||
self.properties_button.setStyleSheet("""
|
||||
QPushButton
|
||||
{
|
||||
font-weight: bold;
|
||||
}
|
||||
""")
|
||||
grid0.addWidget(self.properties_button, 6, 0, 1, 3)
|
||||
|
||||
# PROPERTIES Frame
|
||||
self.properties_frame = QtWidgets.QFrame()
|
||||
self.properties_frame.setContentsMargins(0, 0, 0, 0)
|
||||
grid0.addWidget(self.properties_frame, 7, 0, 1, 3)
|
||||
self.properties_box = QtWidgets.QVBoxLayout()
|
||||
self.properties_box.setContentsMargins(0, 0, 0, 0)
|
||||
self.properties_frame.setLayout(self.properties_box)
|
||||
self.properties_frame.hide()
|
||||
|
||||
self.treeWidget = FCTree(columns=2)
|
||||
|
||||
self.properties_box.addWidget(self.treeWidget)
|
||||
self.properties_box.setStretch(0, 0)
|
||||
|
||||
# ### Gerber Apertures ####
|
||||
self.apertures_table_label = QtWidgets.QLabel('%s:' % _('Apertures'))
|
||||
|
@ -324,6 +345,11 @@ class GerberObjectUI(ObjectUI):
|
|||
)
|
||||
grid0.addWidget(self.create_buffer_button, 12, 0, 1, 3)
|
||||
|
||||
separator_line1 = QtWidgets.QFrame()
|
||||
separator_line1.setFrameShape(QtWidgets.QFrame.HLine)
|
||||
separator_line1.setFrameShadow(QtWidgets.QFrame.Sunken)
|
||||
grid0.addWidget(separator_line1, 13, 0, 1, 3)
|
||||
|
||||
self.tool_lbl = QtWidgets.QLabel('<b>%s</b>' % _("TOOLS"))
|
||||
grid0.addWidget(self.tool_lbl, 14, 0, 1, 3)
|
||||
|
||||
|
@ -538,6 +564,32 @@ class ExcellonObjectUI(ObjectUI):
|
|||
""")
|
||||
grid0.addWidget(self.editor_button, 4, 0, 1, 3)
|
||||
|
||||
# PROPERTIES CB
|
||||
self.properties_button = FCButton('%s' % _("PROPERTIES"), checkable=True)
|
||||
self.properties_button.setIcon(QtGui.QIcon(self.app.resource_location + '/properties32.png'))
|
||||
self.properties_button.setToolTip(_("Show the Properties."))
|
||||
self.properties_button.setStyleSheet("""
|
||||
QPushButton
|
||||
{
|
||||
font-weight: bold;
|
||||
}
|
||||
""")
|
||||
grid0.addWidget(self.properties_button, 6, 0, 1, 3)
|
||||
|
||||
# PROPERTIES Frame
|
||||
self.properties_frame = QtWidgets.QFrame()
|
||||
self.properties_frame.setContentsMargins(0, 0, 0, 0)
|
||||
grid0.addWidget(self.properties_frame, 7, 0, 1, 3)
|
||||
self.properties_box = QtWidgets.QVBoxLayout()
|
||||
self.properties_box.setContentsMargins(0, 0, 0, 0)
|
||||
self.properties_frame.setLayout(self.properties_box)
|
||||
self.properties_frame.hide()
|
||||
|
||||
self.treeWidget = FCTree(columns=2)
|
||||
|
||||
self.properties_box.addWidget(self.treeWidget)
|
||||
self.properties_box.setStretch(0, 0)
|
||||
|
||||
# ### Tools Drills ####
|
||||
self.tools_table_label = QtWidgets.QLabel('<b>%s</b>' % _('Tools Table'))
|
||||
self.tools_table_label.setToolTip(
|
||||
|
@ -561,9 +613,9 @@ class ExcellonObjectUI(ObjectUI):
|
|||
hlay_plot.addStretch()
|
||||
hlay_plot.addWidget(self.plot_cb)
|
||||
|
||||
grid0.addWidget(self.tools_table_label, 6, 0)
|
||||
grid0.addWidget(self.table_visibility_cb, 6, 1)
|
||||
grid0.addLayout(hlay_plot, 6, 2)
|
||||
grid0.addWidget(self.tools_table_label, 8, 0)
|
||||
grid0.addWidget(self.table_visibility_cb, 8, 1)
|
||||
grid0.addLayout(hlay_plot, 8, 2)
|
||||
|
||||
# #############################################################################################################
|
||||
# #############################################################################################################
|
||||
|
@ -657,7 +709,7 @@ class ExcellonObjectUI(ObjectUI):
|
|||
self.milling_button = QtWidgets.QPushButton(_('Milling Tool'))
|
||||
self.milling_button.setIcon(QtGui.QIcon(self.app.resource_location + '/milling_tool32.png'))
|
||||
self.milling_button.setToolTip(
|
||||
_("Generate GCode out of slot holes in an Excellon object.")
|
||||
_("Generate a Geometry for milling drills or slots in an Excellon object.")
|
||||
)
|
||||
self.milling_button.setStyleSheet("""
|
||||
QPushButton
|
||||
|
@ -666,6 +718,8 @@ class ExcellonObjectUI(ObjectUI):
|
|||
}
|
||||
""")
|
||||
grid2.addWidget(self.milling_button, 6, 0, 1, 2)
|
||||
# TODO until the Milling Tool is finished this stays disabled
|
||||
self.milling_button.setDisabled(True)
|
||||
|
||||
separator_line = QtWidgets.QFrame()
|
||||
separator_line.setFrameShape(QtWidgets.QFrame.HLine)
|
||||
|
@ -689,7 +743,7 @@ class ExcellonObjectUI(ObjectUI):
|
|||
)
|
||||
self.grid6.addWidget(self.mill_hole_label, 5, 0, 1, 3)
|
||||
|
||||
self.tdlabel = QtWidgets.QLabel('%s:' % _('Tool Dia'))
|
||||
self.tdlabel = QtWidgets.QLabel('%s:' % _('Milling Diameter'))
|
||||
self.tdlabel.setToolTip(
|
||||
_("Diameter of the cutting tool.")
|
||||
)
|
||||
|
@ -704,7 +758,7 @@ class ExcellonObjectUI(ObjectUI):
|
|||
self.generate_milling_button = QtWidgets.QPushButton(_('Mill Drills'))
|
||||
self.generate_milling_button.setToolTip(
|
||||
_("Create the Geometry Object\n"
|
||||
"for milling DRILLS toolpaths.")
|
||||
"for milling drills.")
|
||||
)
|
||||
self.generate_milling_button.setStyleSheet("""
|
||||
QPushButton
|
||||
|
@ -724,7 +778,7 @@ class ExcellonObjectUI(ObjectUI):
|
|||
self.generate_milling_slots_button = QtWidgets.QPushButton(_('Mill Slots'))
|
||||
self.generate_milling_slots_button.setToolTip(
|
||||
_("Create the Geometry Object\n"
|
||||
"for milling SLOTS toolpaths.")
|
||||
"for milling slots.")
|
||||
)
|
||||
self.generate_milling_slots_button.setStyleSheet("""
|
||||
QPushButton
|
||||
|
@ -814,6 +868,32 @@ class GeometryObjectUI(ObjectUI):
|
|||
""")
|
||||
grid_header.addWidget(self.editor_button, 4, 0, 1, 3)
|
||||
|
||||
# PROPERTIES CB
|
||||
self.properties_button = FCButton('%s' % _("PROPERTIES"), checkable=True)
|
||||
self.properties_button.setIcon(QtGui.QIcon(self.app.resource_location + '/properties32.png'))
|
||||
self.properties_button.setToolTip(_("Show the Properties."))
|
||||
self.properties_button.setStyleSheet("""
|
||||
QPushButton
|
||||
{
|
||||
font-weight: bold;
|
||||
}
|
||||
""")
|
||||
grid_header.addWidget(self.properties_button, 6, 0, 1, 3)
|
||||
|
||||
# PROPERTIES Frame
|
||||
self.properties_frame = QtWidgets.QFrame()
|
||||
self.properties_frame.setContentsMargins(0, 0, 0, 0)
|
||||
grid_header.addWidget(self.properties_frame, 7, 0, 1, 3)
|
||||
self.properties_box = QtWidgets.QVBoxLayout()
|
||||
self.properties_box.setContentsMargins(0, 0, 0, 0)
|
||||
self.properties_frame.setLayout(self.properties_box)
|
||||
self.properties_frame.hide()
|
||||
|
||||
self.treeWidget = FCTree(columns=2)
|
||||
|
||||
self.properties_box.addWidget(self.treeWidget)
|
||||
self.properties_box.setStretch(0, 0)
|
||||
|
||||
# add a frame and inside add a vertical box layout. Inside this vbox layout I add all the Tools widgets
|
||||
# this way I can hide/show the frame
|
||||
self.geo_tools_frame = QtWidgets.QFrame()
|
||||
|
@ -980,8 +1060,9 @@ class GeometryObjectUI(ObjectUI):
|
|||
self.addtool_from_db_btn.setIcon(QtGui.QIcon(self.app.resource_location + '/search_db32.png'))
|
||||
self.addtool_from_db_btn.setToolTip(
|
||||
_("Add a new tool to the Tool Table\n"
|
||||
"from the Tool Database.\n"
|
||||
"Tool database administration in Menu: Options -> Tools Database")
|
||||
"from the Tools Database.\n"
|
||||
"Tools database administration in in:\n"
|
||||
"Menu: Options -> Tools Database")
|
||||
)
|
||||
|
||||
bhlay.addWidget(self.addtool_btn)
|
||||
|
@ -1757,6 +1838,32 @@ class CNCObjectUI(ObjectUI):
|
|||
""")
|
||||
f_lay.addWidget(self.editor_button, 4, 0, 1, 3)
|
||||
|
||||
# PROPERTIES CB
|
||||
self.properties_button = FCButton('%s' % _("PROPERTIES"), checkable=True)
|
||||
self.properties_button.setIcon(QtGui.QIcon(self.app.resource_location + '/properties32.png'))
|
||||
self.properties_button.setToolTip(_("Show the Properties."))
|
||||
self.properties_button.setStyleSheet("""
|
||||
QPushButton
|
||||
{
|
||||
font-weight: bold;
|
||||
}
|
||||
""")
|
||||
f_lay.addWidget(self.properties_button, 6, 0, 1, 3)
|
||||
|
||||
# PROPERTIES Frame
|
||||
self.properties_frame = QtWidgets.QFrame()
|
||||
self.properties_frame.setContentsMargins(0, 0, 0, 0)
|
||||
f_lay.addWidget(self.properties_frame, 7, 0, 1, 3)
|
||||
self.properties_box = QtWidgets.QVBoxLayout()
|
||||
self.properties_box.setContentsMargins(0, 0, 0, 0)
|
||||
self.properties_frame.setLayout(self.properties_box)
|
||||
self.properties_frame.hide()
|
||||
|
||||
self.treeWidget = FCTree(columns=2)
|
||||
|
||||
self.properties_box.addWidget(self.treeWidget)
|
||||
self.properties_box.setStretch(0, 0)
|
||||
|
||||
# Annotation
|
||||
self.annotation_cb = FCCheckBox(_("Display Annotation"))
|
||||
self.annotation_cb.setToolTip(
|
||||
|
@ -1764,12 +1871,12 @@ class CNCObjectUI(ObjectUI):
|
|||
"When checked it will display numbers in order for each end\n"
|
||||
"of a travel line.")
|
||||
)
|
||||
f_lay.addWidget(self.annotation_cb, 6, 0, 1, 3)
|
||||
f_lay.addWidget(self.annotation_cb, 8, 0, 1, 3)
|
||||
|
||||
separator_line = QtWidgets.QFrame()
|
||||
separator_line.setFrameShape(QtWidgets.QFrame.HLine)
|
||||
separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
|
||||
f_lay.addWidget(separator_line, 8, 0, 1, 3)
|
||||
f_lay.addWidget(separator_line, 10, 0, 1, 3)
|
||||
|
||||
# Travelled Distance
|
||||
self.t_distance_label = QtWidgets.QLabel("<b>%s:</b>" % _("Travelled distance"))
|
||||
|
@ -1780,9 +1887,9 @@ class CNCObjectUI(ObjectUI):
|
|||
self.t_distance_entry = FCEntry()
|
||||
self.units_label = QtWidgets.QLabel()
|
||||
|
||||
f_lay.addWidget(self.t_distance_label, 10, 0)
|
||||
f_lay.addWidget(self.t_distance_entry, 10, 1)
|
||||
f_lay.addWidget(self.units_label, 10, 2)
|
||||
f_lay.addWidget(self.t_distance_label, 12, 0)
|
||||
f_lay.addWidget(self.t_distance_entry, 12, 1)
|
||||
f_lay.addWidget(self.units_label, 12, 2)
|
||||
|
||||
# Estimated Time
|
||||
self.t_time_label = QtWidgets.QLabel("<b>%s:</b>" % _("Estimated time"))
|
||||
|
@ -1793,9 +1900,9 @@ class CNCObjectUI(ObjectUI):
|
|||
self.t_time_entry = FCEntry()
|
||||
self.units_time_label = QtWidgets.QLabel()
|
||||
|
||||
f_lay.addWidget(self.t_time_label, 12, 0)
|
||||
f_lay.addWidget(self.t_time_entry, 12, 1)
|
||||
f_lay.addWidget(self.units_time_label, 12, 2)
|
||||
f_lay.addWidget(self.t_time_label, 14, 0)
|
||||
f_lay.addWidget(self.t_time_entry, 14, 1)
|
||||
f_lay.addWidget(self.units_time_label, 14, 2)
|
||||
|
||||
self.t_distance_label.hide()
|
||||
self.t_distance_entry.setVisible(False)
|
||||
|
@ -1805,7 +1912,7 @@ class CNCObjectUI(ObjectUI):
|
|||
separator_line = QtWidgets.QFrame()
|
||||
separator_line.setFrameShape(QtWidgets.QFrame.HLine)
|
||||
separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
|
||||
f_lay.addWidget(separator_line, 14, 0, 1, 3)
|
||||
f_lay.addWidget(separator_line, 16, 0, 1, 3)
|
||||
|
||||
hlay = QtWidgets.QHBoxLayout()
|
||||
self.custom_box.addLayout(hlay)
|
||||
|
|
|
@ -342,6 +342,7 @@ class PreferencesUIManager:
|
|||
|
||||
"tools_iso_rest": self.ui.tools_defaults_form.tools_iso_group.rest_cb,
|
||||
"tools_iso_combine_passes": self.ui.tools_defaults_form.tools_iso_group.combine_passes_cb,
|
||||
"tools_iso_check_valid": self.ui.tools_defaults_form.tools_iso_group.valid_cb,
|
||||
"tools_iso_isoexcept": self.ui.tools_defaults_form.tools_iso_group.except_cb,
|
||||
"tools_iso_selection": self.ui.tools_defaults_form.tools_iso_group.select_combo,
|
||||
"tools_iso_poly_ints": self.ui.tools_defaults_form.tools_iso_group.poly_int_cb,
|
||||
|
@ -381,6 +382,12 @@ class PreferencesUIManager:
|
|||
"tools_drill_f_plunge": self.ui.tools_defaults_form.tools_drill_group.fplunge_cb,
|
||||
"tools_drill_f_retract": self.ui.tools_defaults_form.tools_drill_group.fretract_cb,
|
||||
|
||||
# Area Exclusion
|
||||
"tools_drill_area_exclusion": self.ui.tools_defaults_form.tools_drill_group.exclusion_cb,
|
||||
"tools_drill_area_shape": self.ui.tools_defaults_form.tools_drill_group.area_shape_radio,
|
||||
"tools_drill_area_strategy": self.ui.tools_defaults_form.tools_drill_group.strategy_radio,
|
||||
"tools_drill_area_overz": self.ui.tools_defaults_form.tools_drill_group.over_z_entry,
|
||||
|
||||
# NCC Tool
|
||||
"tools_ncctools": self.ui.tools_defaults_form.tools_ncc_group.ncc_tool_dia_entry,
|
||||
"tools_nccorder": self.ui.tools_defaults_form.tools_ncc_group.ncc_order_radio,
|
||||
|
@ -461,6 +468,7 @@ class PreferencesUIManager:
|
|||
"tools_film_file_type_radio": self.ui.tools_defaults_form.tools_film_group.file_type_radio,
|
||||
"tools_film_orientation": self.ui.tools_defaults_form.tools_film_group.orientation_radio,
|
||||
"tools_film_pagesize": self.ui.tools_defaults_form.tools_film_group.pagesize_combo,
|
||||
"tools_film_png_dpi": self.ui.tools_defaults_form.tools_film_group.png_dpi_spinner,
|
||||
|
||||
# Panelize Tool
|
||||
"tools_panelize_spacing_columns": self.ui.tools_defaults_form.tools_panelize_group.pspacing_columns,
|
||||
|
@ -949,7 +957,7 @@ class PreferencesUIManager:
|
|||
|
||||
self.save_defaults(silent=False)
|
||||
# load the defaults so they are updated into the app
|
||||
self.defaults.load(filename=os.path.join(self.data_path, 'current_defaults.FlatConfig'))
|
||||
self.defaults.load(filename=os.path.join(self.data_path, 'current_defaults.FlatConfig'), inform=self.inform)
|
||||
|
||||
settgs = QSettings("Open Source", "FlatCAM")
|
||||
|
||||
|
|
|
@ -197,13 +197,11 @@ class GeometryAdvOptPrefGroupUI(OptionsGroupUI):
|
|||
# -----------------------------
|
||||
# --- Area Exclusion ----------
|
||||
# -----------------------------
|
||||
self.adv_label = QtWidgets.QLabel('<b>%s:</b>' % _('Area Exclusion'))
|
||||
self.adv_label.setToolTip(
|
||||
_("Area exclusion parameters.\n"
|
||||
"Those parameters are available only for\n"
|
||||
"Advanced App. Level.")
|
||||
self.area_exc_label = QtWidgets.QLabel('<b>%s:</b>' % _('Area Exclusion'))
|
||||
self.area_exc_label.setToolTip(
|
||||
_("Area exclusion parameters.")
|
||||
)
|
||||
grid1.addWidget(self.adv_label, 13, 0, 1, 2)
|
||||
grid1.addWidget(self.area_exc_label, 13, 0, 1, 2)
|
||||
|
||||
# Exclusion Area CB
|
||||
self.exclusion_cb = FCCheckBox('%s' % _("Exclusion areas"))
|
||||
|
|
|
@ -64,9 +64,9 @@ class Tools2InvertPrefGroupUI(OptionsGroupUI):
|
|||
"- bevel -> the lines are joined by a third line")
|
||||
)
|
||||
self.join_radio = RadioSet([
|
||||
{'label': 'Rounded', 'value': 'r'},
|
||||
{'label': 'Square', 'value': 's'},
|
||||
{'label': 'Bevel', 'value': 'b'}
|
||||
{'label': _('Rounded'), 'value': 'r'},
|
||||
{'label': _('Square'), 'value': 's'},
|
||||
{'label': _('Bevel'), 'value': 'b'}
|
||||
], orientation='vertical', stretch=False)
|
||||
|
||||
grid0.addWidget(self.join_label, 5, 0, 1, 2)
|
||||
|
|
|
@ -2,7 +2,7 @@ from PyQt5 import QtWidgets
|
|||
from PyQt5.QtCore import QSettings, Qt
|
||||
|
||||
from appGUI.GUIElements import RadioSet, FCDoubleSpinner, FCComboBox, FCCheckBox, FCSpinner, NumericalEvalTupleEntry, \
|
||||
OptionalInputSection, NumericalEvalEntry
|
||||
OptionalInputSection, NumericalEvalEntry, FCLabel
|
||||
from appGUI.preferences.OptionsGroupUI import OptionsGroupUI
|
||||
|
||||
import gettext
|
||||
|
@ -28,7 +28,7 @@ class ToolsDrillPrefGroupUI(OptionsGroupUI):
|
|||
self.decimals = decimals
|
||||
|
||||
# ## Clear non-copper regions
|
||||
self.drill_label = QtWidgets.QLabel("<b>%s:</b>" % _("Parameters"))
|
||||
self.drill_label = FCLabel("<b>%s:</b>" % _("Parameters"))
|
||||
self.drill_label.setToolTip(
|
||||
_("Create CNCJob with toolpaths for drilling or milling holes.")
|
||||
)
|
||||
|
@ -38,7 +38,7 @@ class ToolsDrillPrefGroupUI(OptionsGroupUI):
|
|||
self.layout.addLayout(grid0)
|
||||
|
||||
# Tool order Radio Button
|
||||
self.order_label = QtWidgets.QLabel('%s:' % _('Tool order'))
|
||||
self.order_label = FCLabel('%s:' % _('Tool order'))
|
||||
self.order_label.setToolTip(_("This set the way that the tools in the tools table are used.\n"
|
||||
"'No' --> means that the used order is the one in the tool table\n"
|
||||
"'Forward' --> means that the tools will be ordered from small to big\n"
|
||||
|
@ -54,7 +54,7 @@ class ToolsDrillPrefGroupUI(OptionsGroupUI):
|
|||
grid0.addWidget(self.order_radio, 1, 1, 1, 2)
|
||||
|
||||
# Cut Z
|
||||
cutzlabel = QtWidgets.QLabel('%s:' % _('Cut Z'))
|
||||
cutzlabel = FCLabel('%s:' % _('Cut Z'))
|
||||
cutzlabel.setToolTip(
|
||||
_("Drill depth (negative)\n"
|
||||
"below the copper surface.")
|
||||
|
@ -95,7 +95,7 @@ class ToolsDrillPrefGroupUI(OptionsGroupUI):
|
|||
grid0.addWidget(self.maxdepth_entry, 4, 1, 1, 2)
|
||||
|
||||
# Travel Z
|
||||
travelzlabel = QtWidgets.QLabel('%s:' % _('Travel Z'))
|
||||
travelzlabel = FCLabel('%s:' % _('Travel Z'))
|
||||
travelzlabel.setToolTip(
|
||||
_("Tool height when travelling\n"
|
||||
"across the XY plane.")
|
||||
|
@ -121,7 +121,7 @@ class ToolsDrillPrefGroupUI(OptionsGroupUI):
|
|||
grid0.addWidget(self.toolchange_cb, 6, 0, 1, 3)
|
||||
|
||||
# Tool Change Z
|
||||
toolchangezlabel = QtWidgets.QLabel('%s:' % _('Toolchange Z'))
|
||||
toolchangezlabel = FCLabel('%s:' % _('Toolchange Z'))
|
||||
toolchangezlabel.setToolTip(
|
||||
_("Z-axis position (height) for\n"
|
||||
"tool change.")
|
||||
|
@ -139,7 +139,7 @@ class ToolsDrillPrefGroupUI(OptionsGroupUI):
|
|||
grid0.addWidget(self.toolchangez_entry, 7, 1, 1, 2)
|
||||
|
||||
# End Move Z
|
||||
endz_label = QtWidgets.QLabel('%s:' % _('End move Z'))
|
||||
endz_label = FCLabel('%s:' % _('End move Z'))
|
||||
endz_label.setToolTip(
|
||||
_("Height of the tool after\n"
|
||||
"the last move at the end of the job.")
|
||||
|
@ -156,7 +156,7 @@ class ToolsDrillPrefGroupUI(OptionsGroupUI):
|
|||
grid0.addWidget(self.endz_entry, 8, 1, 1, 2)
|
||||
|
||||
# End Move X,Y
|
||||
endmove_xy_label = QtWidgets.QLabel('%s:' % _('End move X,Y'))
|
||||
endmove_xy_label = FCLabel('%s:' % _('End move X,Y'))
|
||||
endmove_xy_label.setToolTip(
|
||||
_("End move X,Y position. In format (x,y).\n"
|
||||
"If no value is entered then there is no move\n"
|
||||
|
@ -168,7 +168,7 @@ class ToolsDrillPrefGroupUI(OptionsGroupUI):
|
|||
grid0.addWidget(self.endxy_entry, 9, 1, 1, 2)
|
||||
|
||||
# Feedrate Z
|
||||
frlabel = QtWidgets.QLabel('%s:' % _('Feedrate Z'))
|
||||
frlabel = FCLabel('%s:' % _('Feedrate Z'))
|
||||
frlabel.setToolTip(
|
||||
_("Tool speed while drilling\n"
|
||||
"(in units per minute).\n"
|
||||
|
@ -183,7 +183,7 @@ class ToolsDrillPrefGroupUI(OptionsGroupUI):
|
|||
grid0.addWidget(self.feedrate_z_entry, 10, 1, 1, 2)
|
||||
|
||||
# Spindle speed
|
||||
spdlabel = QtWidgets.QLabel('%s:' % _('Spindle Speed'))
|
||||
spdlabel = FCLabel('%s:' % _('Spindle Speed'))
|
||||
spdlabel.setToolTip(
|
||||
_("Speed of the spindle\n"
|
||||
"in RPM (optional)")
|
||||
|
@ -206,7 +206,7 @@ class ToolsDrillPrefGroupUI(OptionsGroupUI):
|
|||
grid0.addWidget(self.dwell_cb, 12, 0, 1, 3)
|
||||
|
||||
# Dwell Time
|
||||
dwelltime = QtWidgets.QLabel('%s:' % _('Duration'))
|
||||
dwelltime = FCLabel('%s:' % _('Duration'))
|
||||
dwelltime.setToolTip(_("Number of time units for spindle to dwell."))
|
||||
self.dwelltime_entry = FCDoubleSpinner()
|
||||
self.dwelltime_entry.set_precision(self.decimals)
|
||||
|
@ -218,7 +218,7 @@ class ToolsDrillPrefGroupUI(OptionsGroupUI):
|
|||
self.ois_dwell_exc = OptionalInputSection(self.dwell_cb, [self.dwelltime_entry])
|
||||
|
||||
# preprocessor selection
|
||||
pp_excellon_label = QtWidgets.QLabel('%s:' % _("Preprocessor"))
|
||||
pp_excellon_label = FCLabel('%s:' % _("Preprocessor"))
|
||||
pp_excellon_label.setToolTip(
|
||||
_("The preprocessor JSON file that dictates\n"
|
||||
"Gcode output.")
|
||||
|
@ -236,7 +236,7 @@ class ToolsDrillPrefGroupUI(OptionsGroupUI):
|
|||
grid0.addWidget(separator_line, 16, 0, 1, 3)
|
||||
|
||||
# DRILL SLOTS LABEL
|
||||
self.dslots_label = QtWidgets.QLabel('<b>%s:</b>' % _('Drilling Slots'))
|
||||
self.dslots_label = FCLabel('<b>%s:</b>' % _('Drilling Slots'))
|
||||
grid0.addWidget(self.dslots_label, 18, 0, 1, 3)
|
||||
|
||||
# Drill slots
|
||||
|
@ -247,7 +247,7 @@ class ToolsDrillPrefGroupUI(OptionsGroupUI):
|
|||
grid0.addWidget(self.drill_slots_cb, 20, 0, 1, 3)
|
||||
|
||||
# Drill Overlap
|
||||
self.drill_overlap_label = QtWidgets.QLabel('%s:' % _('Overlap'))
|
||||
self.drill_overlap_label = FCLabel('%s:' % _('Overlap'))
|
||||
self.drill_overlap_label.setToolTip(
|
||||
_("How much (percentage) of the tool diameter to overlap previous drill hole.")
|
||||
)
|
||||
|
@ -273,14 +273,14 @@ class ToolsDrillPrefGroupUI(OptionsGroupUI):
|
|||
separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
|
||||
grid0.addWidget(separator_line, 26, 0, 1, 3)
|
||||
|
||||
self.exc_label = QtWidgets.QLabel('<b>%s:</b>' % _('Advanced Options'))
|
||||
self.exc_label = FCLabel('<b>%s:</b>' % _('Advanced Options'))
|
||||
self.exc_label.setToolTip(
|
||||
_("A list of advanced parameters.")
|
||||
)
|
||||
grid0.addWidget(self.exc_label, 28, 0, 1, 3)
|
||||
|
||||
# Offset Z
|
||||
offsetlabel = QtWidgets.QLabel('%s:' % _('Offset Z'))
|
||||
offsetlabel = FCLabel('%s:' % _('Offset Z'))
|
||||
offsetlabel.setToolTip(
|
||||
_("Some drill bits (the larger ones) need to drill deeper\n"
|
||||
"to create the desired exit hole diameter due of the tip shape.\n"
|
||||
|
@ -293,7 +293,7 @@ class ToolsDrillPrefGroupUI(OptionsGroupUI):
|
|||
grid0.addWidget(self.offset_entry, 29, 1, 1, 2)
|
||||
|
||||
# ToolChange X,Y
|
||||
toolchange_xy_label = QtWidgets.QLabel('%s:' % _('Toolchange X,Y'))
|
||||
toolchange_xy_label = FCLabel('%s:' % _('Toolchange X,Y'))
|
||||
toolchange_xy_label.setToolTip(
|
||||
_("Toolchange X,Y position.")
|
||||
)
|
||||
|
@ -303,7 +303,7 @@ class ToolsDrillPrefGroupUI(OptionsGroupUI):
|
|||
grid0.addWidget(self.toolchangexy_entry, 31, 1, 1, 2)
|
||||
|
||||
# Start Z
|
||||
startzlabel = QtWidgets.QLabel('%s:' % _('Start Z'))
|
||||
startzlabel = FCLabel('%s:' % _('Start Z'))
|
||||
startzlabel.setToolTip(
|
||||
_("Height of the tool just after start.\n"
|
||||
"Delete the value if you don't need this feature.")
|
||||
|
@ -314,7 +314,7 @@ class ToolsDrillPrefGroupUI(OptionsGroupUI):
|
|||
grid0.addWidget(self.estartz_entry, 33, 1, 1, 2)
|
||||
|
||||
# Feedrate Rapids
|
||||
fr_rapid_label = QtWidgets.QLabel('%s:' % _('Feedrate Rapids'))
|
||||
fr_rapid_label = FCLabel('%s:' % _('Feedrate Rapids'))
|
||||
fr_rapid_label.setToolTip(
|
||||
_("Tool speed while drilling\n"
|
||||
"(in units per minute).\n"
|
||||
|
@ -330,7 +330,7 @@ class ToolsDrillPrefGroupUI(OptionsGroupUI):
|
|||
grid0.addWidget(self.feedrate_rapid_entry, 35, 1, 1, 2)
|
||||
|
||||
# Probe depth
|
||||
self.pdepth_label = QtWidgets.QLabel('%s:' % _("Probe Z depth"))
|
||||
self.pdepth_label = FCLabel('%s:' % _("Probe Z depth"))
|
||||
self.pdepth_label.setToolTip(
|
||||
_("The maximum depth that the probe is allowed\n"
|
||||
"to probe. Negative value, in current units.")
|
||||
|
@ -343,7 +343,7 @@ class ToolsDrillPrefGroupUI(OptionsGroupUI):
|
|||
grid0.addWidget(self.pdepth_entry, 37, 1, 1, 2)
|
||||
|
||||
# Probe feedrate
|
||||
self.feedrate_probe_label = QtWidgets.QLabel('%s:' % _("Feedrate Probe"))
|
||||
self.feedrate_probe_label = FCLabel('%s:' % _("Feedrate Probe"))
|
||||
self.feedrate_probe_label.setToolTip(
|
||||
_("The feedrate used while the probe is probing.")
|
||||
)
|
||||
|
@ -355,7 +355,7 @@ class ToolsDrillPrefGroupUI(OptionsGroupUI):
|
|||
grid0.addWidget(self.feedrate_probe_entry, 38, 1, 1, 2)
|
||||
|
||||
# Spindle direction
|
||||
spindle_dir_label = QtWidgets.QLabel('%s:' % _('Spindle direction'))
|
||||
spindle_dir_label = FCLabel('%s:' % _('Spindle direction'))
|
||||
spindle_dir_label.setToolTip(
|
||||
_("This sets the direction that the spindle is rotating.\n"
|
||||
"It can be either:\n"
|
||||
|
@ -389,4 +389,64 @@ class ToolsDrillPrefGroupUI(OptionsGroupUI):
|
|||
|
||||
grid0.addWidget(self.fretract_cb, 45, 0, 1, 3)
|
||||
|
||||
separator_line = QtWidgets.QFrame()
|
||||
separator_line.setFrameShape(QtWidgets.QFrame.HLine)
|
||||
separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
|
||||
grid0.addWidget(separator_line, 46, 0, 1, 3)
|
||||
|
||||
# -----------------------------
|
||||
# --- Area Exclusion ----------
|
||||
# -----------------------------
|
||||
self.area_exc_label = FCLabel('<b>%s:</b>' % _('Area Exclusion'))
|
||||
self.area_exc_label.setToolTip(
|
||||
_("Area exclusion parameters.")
|
||||
)
|
||||
grid0.addWidget(self.area_exc_label, 47, 0, 1, 2)
|
||||
|
||||
# Exclusion Area CB
|
||||
self.exclusion_cb = FCCheckBox('%s' % _("Exclusion areas"))
|
||||
self.exclusion_cb.setToolTip(
|
||||
_(
|
||||
"Include exclusion areas.\n"
|
||||
"In those areas the travel of the tools\n"
|
||||
"is forbidden."
|
||||
)
|
||||
)
|
||||
grid0.addWidget(self.exclusion_cb, 49, 0, 1, 2)
|
||||
|
||||
# Area Selection shape
|
||||
self.area_shape_label = FCLabel('%s:' % _("Shape"))
|
||||
self.area_shape_label.setToolTip(
|
||||
_("The kind of selection shape used for area selection.")
|
||||
)
|
||||
|
||||
self.area_shape_radio = RadioSet([{'label': _("Square"), 'value': 'square'},
|
||||
{'label': _("Polygon"), 'value': 'polygon'}])
|
||||
|
||||
grid0.addWidget(self.area_shape_label, 51, 0)
|
||||
grid0.addWidget(self.area_shape_radio, 51, 1)
|
||||
|
||||
# Chose Strategy
|
||||
self.strategy_label = FCLabel('%s:' % _("Strategy"))
|
||||
self.strategy_label.setToolTip(_("The strategy followed when encountering an exclusion area.\n"
|
||||
"Can be:\n"
|
||||
"- Over -> when encountering the area, the tool will go to a set height\n"
|
||||
"- Around -> will avoid the exclusion area by going around the area"))
|
||||
self.strategy_radio = RadioSet([{'label': _('Over'), 'value': 'over'},
|
||||
{'label': _('Around'), 'value': 'around'}])
|
||||
|
||||
grid0.addWidget(self.strategy_label, 53, 0)
|
||||
grid0.addWidget(self.strategy_radio, 53, 1)
|
||||
|
||||
# Over Z
|
||||
self.over_z_label = FCLabel('%s:' % _("Over Z"))
|
||||
self.over_z_label.setToolTip(_("The height Z to which the tool will rise in order to avoid\n"
|
||||
"an interdiction area."))
|
||||
self.over_z_entry = FCDoubleSpinner()
|
||||
self.over_z_entry.set_range(0.000, 9999.9999)
|
||||
self.over_z_entry.set_precision(self.decimals)
|
||||
|
||||
grid0.addWidget(self.over_z_label, 55, 0)
|
||||
grid0.addWidget(self.over_z_entry, 55, 1)
|
||||
|
||||
self.layout.addStretch()
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
from PyQt5 import QtWidgets, QtCore, QtGui
|
||||
from PyQt5.QtCore import Qt, QSettings
|
||||
from PyQt5 import QtWidgets
|
||||
from PyQt5.QtCore import QSettings
|
||||
|
||||
from appGUI.GUIElements import RadioSet, FCEntry, FCDoubleSpinner, FCCheckBox, FCComboBox, FCColorEntry
|
||||
from appGUI.GUIElements import RadioSet, FCDoubleSpinner, FCCheckBox, FCComboBox, FCColorEntry, FCLabel, FCSpinner
|
||||
from appGUI.preferences.OptionsGroupUI import OptionsGroupUI
|
||||
|
||||
import gettext
|
||||
|
@ -28,7 +28,7 @@ class ToolsFilmPrefGroupUI(OptionsGroupUI):
|
|||
self.decimals = decimals
|
||||
|
||||
# ## Parameters
|
||||
self.film_label = QtWidgets.QLabel("<b>%s:</b>" % _("Parameters"))
|
||||
self.film_label = FCLabel("<b>%s:</b>" % _("Parameters"))
|
||||
self.film_label.setToolTip(
|
||||
_("Create a PCB film from a Gerber or Geometry object.\n"
|
||||
"The file is saved in SVG format.")
|
||||
|
@ -40,7 +40,7 @@ class ToolsFilmPrefGroupUI(OptionsGroupUI):
|
|||
|
||||
self.film_type_radio = RadioSet([{'label': 'Pos', 'value': 'pos'},
|
||||
{'label': 'Neg', 'value': 'neg'}])
|
||||
ftypelbl = QtWidgets.QLabel('%s:' % _('Film Type'))
|
||||
ftypelbl = FCLabel('%s:' % _('Film Type'))
|
||||
ftypelbl.setToolTip(
|
||||
_("Generate a Positive black film or a Negative film.\n"
|
||||
"Positive means that it will print the features\n"
|
||||
|
@ -53,7 +53,7 @@ class ToolsFilmPrefGroupUI(OptionsGroupUI):
|
|||
grid0.addWidget(self.film_type_radio, 0, 1)
|
||||
|
||||
# Film Color
|
||||
self.film_color_label = QtWidgets.QLabel('%s:' % _('Film Color'))
|
||||
self.film_color_label = FCLabel('%s:' % _('Film Color'))
|
||||
self.film_color_label.setToolTip(
|
||||
_("Set the film color when positive film is selected.")
|
||||
)
|
||||
|
@ -68,7 +68,7 @@ class ToolsFilmPrefGroupUI(OptionsGroupUI):
|
|||
self.film_boundary_entry.set_range(0, 9999.9999)
|
||||
self.film_boundary_entry.setSingleStep(0.1)
|
||||
|
||||
self.film_boundary_label = QtWidgets.QLabel('%s:' % _("Border"))
|
||||
self.film_boundary_label = FCLabel('%s:' % _("Border"))
|
||||
self.film_boundary_label.setToolTip(
|
||||
_("Specify a border around the object.\n"
|
||||
"Only for negative film.\n"
|
||||
|
@ -87,7 +87,7 @@ class ToolsFilmPrefGroupUI(OptionsGroupUI):
|
|||
self.film_scale_stroke_entry.set_range(0, 9999.9999)
|
||||
self.film_scale_stroke_entry.setSingleStep(0.1)
|
||||
|
||||
self.film_scale_stroke_label = QtWidgets.QLabel('%s:' % _("Scale Stroke"))
|
||||
self.film_scale_stroke_label = FCLabel('%s:' % _("Scale Stroke"))
|
||||
self.film_scale_stroke_label.setToolTip(
|
||||
_("Scale the line stroke thickness of each feature in the SVG file.\n"
|
||||
"It means that the line that envelope each SVG feature will be thicker or thinner,\n"
|
||||
|
@ -96,7 +96,7 @@ class ToolsFilmPrefGroupUI(OptionsGroupUI):
|
|||
grid0.addWidget(self.film_scale_stroke_label, 3, 0)
|
||||
grid0.addWidget(self.film_scale_stroke_entry, 3, 1)
|
||||
|
||||
self.film_adj_label = QtWidgets.QLabel('<b>%s</b>' % _("Film Adjustments"))
|
||||
self.film_adj_label = FCLabel('<b>%s</b>' % _("Film Adjustments"))
|
||||
self.film_adj_label.setToolTip(
|
||||
_("Sometime the printers will distort the print shape, especially the Laser types.\n"
|
||||
"This section provide the tools to compensate for the print distortions.")
|
||||
|
@ -117,7 +117,7 @@ class ToolsFilmPrefGroupUI(OptionsGroupUI):
|
|||
)
|
||||
grid0.addWidget(self.film_scale_cb, 5, 0, 1, 2)
|
||||
|
||||
self.film_scalex_label = QtWidgets.QLabel('%s:' % _("X factor"))
|
||||
self.film_scalex_label = FCLabel('%s:' % _("X factor"))
|
||||
self.film_scalex_entry = FCDoubleSpinner()
|
||||
self.film_scalex_entry.set_range(-999.9999, 999.9999)
|
||||
self.film_scalex_entry.set_precision(self.decimals)
|
||||
|
@ -126,7 +126,7 @@ class ToolsFilmPrefGroupUI(OptionsGroupUI):
|
|||
grid0.addWidget(self.film_scalex_label, 6, 0)
|
||||
grid0.addWidget(self.film_scalex_entry, 6, 1)
|
||||
|
||||
self.film_scaley_label = QtWidgets.QLabel('%s:' % _("Y factor"))
|
||||
self.film_scaley_label = FCLabel('%s:' % _("Y factor"))
|
||||
self.film_scaley_entry = FCDoubleSpinner()
|
||||
self.film_scaley_entry.set_range(-999.9999, 999.9999)
|
||||
self.film_scaley_entry.set_precision(self.decimals)
|
||||
|
@ -148,7 +148,7 @@ class ToolsFilmPrefGroupUI(OptionsGroupUI):
|
|||
)
|
||||
grid0.addWidget(self.film_skew_cb, 8, 0, 1, 2)
|
||||
|
||||
self.film_skewx_label = QtWidgets.QLabel('%s:' % _("X angle"))
|
||||
self.film_skewx_label = FCLabel('%s:' % _("X angle"))
|
||||
self.film_skewx_entry = FCDoubleSpinner()
|
||||
self.film_skewx_entry.set_range(-999.9999, 999.9999)
|
||||
self.film_skewx_entry.set_precision(self.decimals)
|
||||
|
@ -157,7 +157,7 @@ class ToolsFilmPrefGroupUI(OptionsGroupUI):
|
|||
grid0.addWidget(self.film_skewx_label, 9, 0)
|
||||
grid0.addWidget(self.film_skewx_entry, 9, 1)
|
||||
|
||||
self.film_skewy_label = QtWidgets.QLabel('%s:' % _("Y angle"))
|
||||
self.film_skewy_label = FCLabel('%s:' % _("Y angle"))
|
||||
self.film_skewy_entry = FCDoubleSpinner()
|
||||
self.film_skewy_entry.set_range(-999.9999, 999.9999)
|
||||
self.film_skewy_entry.set_precision(self.decimals)
|
||||
|
@ -166,7 +166,7 @@ class ToolsFilmPrefGroupUI(OptionsGroupUI):
|
|||
grid0.addWidget(self.film_skewy_label, 10, 0)
|
||||
grid0.addWidget(self.film_skewy_entry, 10, 1)
|
||||
|
||||
self.film_skew_ref_label = QtWidgets.QLabel('%s:' % _("Reference"))
|
||||
self.film_skew_ref_label = FCLabel('%s:' % _("Reference"))
|
||||
self.film_skew_ref_label.setToolTip(
|
||||
_("The reference point to be used as origin for the skew.\n"
|
||||
"It can be one of the four points of the geometry bounding box.")
|
||||
|
@ -198,7 +198,7 @@ class ToolsFilmPrefGroupUI(OptionsGroupUI):
|
|||
{'label': _('Y'), 'value': 'y'},
|
||||
{'label': _('Both'), 'value': 'both'}],
|
||||
stretch=False)
|
||||
self.film_mirror_axis_label = QtWidgets.QLabel('%s:' % _("Mirror axis"))
|
||||
self.film_mirror_axis_label = FCLabel('%s:' % _("Mirror axis"))
|
||||
|
||||
grid0.addWidget(self.film_mirror_axis_label, 13, 0)
|
||||
grid0.addWidget(self.film_mirror_axis, 13, 1)
|
||||
|
@ -213,7 +213,7 @@ class ToolsFilmPrefGroupUI(OptionsGroupUI):
|
|||
{'label': _('PDF'), 'value': 'pdf'}
|
||||
], stretch=False)
|
||||
|
||||
self.file_type_label = QtWidgets.QLabel(_("Film Type:"))
|
||||
self.file_type_label = FCLabel(_("Film Type:"))
|
||||
self.file_type_label.setToolTip(
|
||||
_("The file type of the saved film. Can be:\n"
|
||||
"- 'SVG' -> open-source vectorial format\n"
|
||||
|
@ -224,7 +224,7 @@ class ToolsFilmPrefGroupUI(OptionsGroupUI):
|
|||
grid0.addWidget(self.file_type_radio, 15, 1)
|
||||
|
||||
# Page orientation
|
||||
self.orientation_label = QtWidgets.QLabel('%s:' % _("Page Orientation"))
|
||||
self.orientation_label = FCLabel('%s:' % _("Page Orientation"))
|
||||
self.orientation_label.setToolTip(_("Can be:\n"
|
||||
"- Portrait\n"
|
||||
"- Landscape"))
|
||||
|
@ -237,7 +237,7 @@ class ToolsFilmPrefGroupUI(OptionsGroupUI):
|
|||
grid0.addWidget(self.orientation_radio, 16, 1)
|
||||
|
||||
# Page Size
|
||||
self.pagesize_label = QtWidgets.QLabel('%s:' % _("Page Size"))
|
||||
self.pagesize_label = FCLabel('%s:' % _("Page Size"))
|
||||
self.pagesize_label.setToolTip(_("A selection of standard ISO 216 page sizes."))
|
||||
|
||||
self.pagesize_combo = FCComboBox()
|
||||
|
@ -302,6 +302,17 @@ class ToolsFilmPrefGroupUI(OptionsGroupUI):
|
|||
grid0.addWidget(self.pagesize_label, 17, 0)
|
||||
grid0.addWidget(self.pagesize_combo, 17, 1)
|
||||
|
||||
# PNG DPI
|
||||
self.png_dpi_label = FCLabel('%s:' % "PNG DPI")
|
||||
self.png_dpi_label.setToolTip(
|
||||
_("Default value is 96 DPI. Change this value to scale the PNG file.")
|
||||
)
|
||||
self.png_dpi_spinner = FCSpinner()
|
||||
self.png_dpi_spinner.set_range(0, 100000)
|
||||
|
||||
grid0.addWidget(self.png_dpi_label, 19, 0)
|
||||
grid0.addWidget(self.png_dpi_spinner, 19, 1)
|
||||
|
||||
self.layout.addStretch()
|
||||
|
||||
# Film Tool
|
||||
|
|
|
@ -271,6 +271,16 @@ class ToolsISOPrefGroupUI(OptionsGroupUI):
|
|||
self.except_cb.setObjectName("i_except")
|
||||
grid0.addWidget(self.except_cb, 17, 2)
|
||||
|
||||
# Check Tool validity
|
||||
self.valid_cb = FCCheckBox(label=_('Check validity'))
|
||||
self.valid_cb.setToolTip(
|
||||
_("If checked then the tools diameters are verified\n"
|
||||
"if they will provide a complete isolation.")
|
||||
)
|
||||
self.valid_cb.setObjectName("i_check")
|
||||
|
||||
grid0.addWidget(self.valid_cb, 18, 0, 1, 3)
|
||||
|
||||
# Isolation Scope
|
||||
self.select_label = QtWidgets.QLabel('%s:' % _("Selection"))
|
||||
self.select_label.setToolTip(
|
||||
|
|
|
@ -574,8 +574,14 @@ class CNCJobObject(FlatCAMObj, CNCjob):
|
|||
self.ui.updateplot_button.clicked.connect(self.on_updateplot_button_click)
|
||||
self.ui.export_gcode_button.clicked.connect(self.on_exportgcode_button_click)
|
||||
self.ui.review_gcode_button.clicked.connect(self.on_edit_code_click)
|
||||
|
||||
# Editor Signal
|
||||
self.ui.editor_button.clicked.connect(lambda: self.app.object2editor())
|
||||
|
||||
# Properties
|
||||
self.ui.properties_button.toggled.connect(self.on_properties)
|
||||
self.calculations_finished.connect(self.update_area_chull)
|
||||
|
||||
# autolevelling signals
|
||||
self.ui.sal_cb.stateChanged.connect(self.on_toggle_autolevelling)
|
||||
self.ui.al_mode_radio.activated_custom.connect(self.on_mode_radio)
|
||||
|
@ -699,6 +705,20 @@ class CNCJobObject(FlatCAMObj, CNCjob):
|
|||
except (TypeError, AttributeError):
|
||||
pass
|
||||
|
||||
def on_properties(self, state):
|
||||
if state:
|
||||
self.ui.properties_frame.show()
|
||||
else:
|
||||
self.ui.properties_frame.hide()
|
||||
return
|
||||
|
||||
self.ui.treeWidget.clear()
|
||||
self.add_properties_items(obj=self, treeWidget=self.ui.treeWidget)
|
||||
|
||||
self.ui.treeWidget.setSizePolicy(QtWidgets.QSizePolicy.Ignored, QtWidgets.QSizePolicy.MinimumExpanding)
|
||||
# make sure that the FCTree widget columns are resized to content
|
||||
self.ui.treeWidget.resize_sig.emit()
|
||||
|
||||
def on_add_al_probepoints(self):
|
||||
# create the solid_geo
|
||||
|
||||
|
@ -1260,7 +1280,7 @@ class CNCJobObject(FlatCAMObj, CNCjob):
|
|||
pass
|
||||
|
||||
answer = self.on_grbl_wake()
|
||||
answer = ['ok'] # hack for development without a GRBL controller connected
|
||||
answer = ['ok'] # FIXME: hack for development without a GRBL controller connected
|
||||
for line in answer:
|
||||
if 'ok' in line.lower():
|
||||
self.ui.com_connect_button.setStyleSheet("QPushButton {background-color: seagreen;}")
|
||||
|
@ -2548,7 +2568,7 @@ class CNCJobObject(FlatCAMObj, CNCjob):
|
|||
# g['geom'] = affinity.scale(g['geom'], factor, factor, origin=(0, 0))
|
||||
#
|
||||
# tool_dia_copy['gcode_parsed'] = deepcopy(dia_value)
|
||||
# tool_dia_copy['solid_geometry'] = cascaded_union([geo['geom'] for geo in dia_value])
|
||||
# tool_dia_copy['solid_geometry'] = unary_union([geo['geom'] for geo in dia_value])
|
||||
|
||||
temp_tools_dict.update({
|
||||
tooluid_key: deepcopy(tool_dia_copy)
|
||||
|
|
|
@ -176,7 +176,11 @@ class ExcellonObject(FlatCAMObj, Excellon):
|
|||
|
||||
# Editor
|
||||
self.ui.editor_button.clicked.connect(lambda: self.app.object2editor())
|
||||
|
||||
|
||||
# Properties
|
||||
self.ui.properties_button.toggled.connect(self.on_properties)
|
||||
self.calculations_finished.connect(self.update_area_chull)
|
||||
|
||||
self.ui.drill_button.clicked.connect(lambda: self.app.drilling_tool.run(toggle=True))
|
||||
# self.ui.milling_button.clicked.connect(lambda: self.app.milling_tool.run(toggle=True))
|
||||
|
||||
|
@ -438,6 +442,9 @@ class ExcellonObject(FlatCAMObj, Excellon):
|
|||
self.ui.slot_tooldia_entry.setDisabled(False)
|
||||
self.ui.generate_milling_slots_button.setDisabled(False)
|
||||
|
||||
# update the milling section
|
||||
self.on_row_selection_change()
|
||||
|
||||
self.ui_connect()
|
||||
|
||||
def ui_connect(self):
|
||||
|
@ -510,12 +517,22 @@ class ExcellonObject(FlatCAMObj, Excellon):
|
|||
self.ui.slot_tooldia_entry.setDisabled(False)
|
||||
self.ui.generate_milling_slots_button.setDisabled(False)
|
||||
|
||||
# find if we have drills:
|
||||
has_drills = None
|
||||
for tt in self.tools:
|
||||
if 'drills' in self.tools[tt] and self.tools[tt]['drills']:
|
||||
has_drills = True
|
||||
break
|
||||
has_drills = True
|
||||
has_slots = True
|
||||
for row in sel_rows:
|
||||
row_dia = self.app.dec_format(float(self.ui.tools_table.item(row, 1).text()), self.decimals)
|
||||
|
||||
for tt in self.tools:
|
||||
tool_dia = self.app.dec_format(float(self.tools[tt]['tooldia']), self.decimals)
|
||||
if tool_dia == row_dia:
|
||||
# find if we have drills:
|
||||
if 'drills' not in self.tools[tt] or not self.tools[tt]['drills']:
|
||||
has_drills = None
|
||||
|
||||
# find if we have slots
|
||||
if 'slots' not in self.tools[tt] or not self.tools[tt]['slots']:
|
||||
has_slots = None
|
||||
|
||||
if has_drills is None:
|
||||
self.ui.tooldia_entry.setDisabled(True)
|
||||
self.ui.generate_milling_button.setDisabled(True)
|
||||
|
@ -523,12 +540,6 @@ class ExcellonObject(FlatCAMObj, Excellon):
|
|||
self.ui.tooldia_entry.setDisabled(False)
|
||||
self.ui.generate_milling_button.setDisabled(False)
|
||||
|
||||
# find if we have slots
|
||||
has_slots = None
|
||||
for tt in self.tools:
|
||||
if 'slots' in self.tools[tt] and self.tools[tt]['slots']:
|
||||
has_slots = True
|
||||
break
|
||||
if has_slots is None:
|
||||
self.ui.slot_tooldia_entry.setDisabled(True)
|
||||
self.ui.generate_milling_slots_button.setDisabled(True)
|
||||
|
@ -603,6 +614,19 @@ class ExcellonObject(FlatCAMObj, Excellon):
|
|||
def on_table_visibility_toggle(self, state):
|
||||
self.ui.tools_table.show() if state else self.ui.tools_table.hide()
|
||||
|
||||
def on_properties(self, state):
|
||||
if state:
|
||||
self.ui.properties_frame.show()
|
||||
else:
|
||||
self.ui.properties_frame.hide()
|
||||
return
|
||||
|
||||
self.ui.treeWidget.clear()
|
||||
self.add_properties_items(obj=self, treeWidget=self.ui.treeWidget)
|
||||
|
||||
# make sure that the FCTree widget columns are resized to content
|
||||
self.ui.treeWidget.resize_sig.emit()
|
||||
|
||||
def export_excellon(self, whole, fract, e_zeros=None, form='dec', factor=1, slot_type='routing'):
|
||||
"""
|
||||
Returns two values, first is a boolean , if 1 then the file has slots and second contain the Excellon code
|
||||
|
@ -878,7 +902,7 @@ class ExcellonObject(FlatCAMObj, Excellon):
|
|||
geo_obj.options['Tools_in_use'] = tool_table_items
|
||||
geo_obj.options['type'] = 'Excellon Geometry'
|
||||
geo_obj.options["cnctooldia"] = str(tooldia)
|
||||
geo_obj.options["multidepth"] = self.options["multidepth"]
|
||||
geo_obj.options["multidepth"] = self.app.defaults["geometry_multidepth"]
|
||||
geo_obj.solid_geometry = []
|
||||
|
||||
# in case that the tool used has the same diameter with the hole, and since the maximum resolution
|
||||
|
@ -978,7 +1002,7 @@ class ExcellonObject(FlatCAMObj, Excellon):
|
|||
geo_obj.options['Tools_in_use'] = tool_table_items
|
||||
geo_obj.options['type'] = 'Excellon Geometry'
|
||||
geo_obj.options["cnctooldia"] = str(tooldia)
|
||||
geo_obj.options["multidepth"] = self.options["multidepth"]
|
||||
geo_obj.options["multidepth"] = self.app.defaults["geometry_multidepth"]
|
||||
geo_obj.solid_geometry = []
|
||||
|
||||
# in case that the tool used has the same diameter with the hole, and since the maximum resolution
|
||||
|
|
|
@ -438,6 +438,11 @@ class GeometryObject(FlatCAMObj, Geometry):
|
|||
"area_shape": self.ui.area_shape_radio,
|
||||
"area_strategy": self.ui.strategy_radio,
|
||||
"area_overz": self.ui.over_z_entry,
|
||||
"polish": self.ui.polish_cb,
|
||||
"polish_dia": self.ui.polish_dia_entry,
|
||||
"polish_pressure": self.ui.polish_pressure_entry,
|
||||
"polish_overlap": self.ui.polish_over_entry,
|
||||
"polish_method": self.ui.polish_method_combo,
|
||||
})
|
||||
|
||||
self.param_fields.update({
|
||||
|
@ -589,8 +594,13 @@ class GeometryObject(FlatCAMObj, Geometry):
|
|||
self.ui.plot_cb.stateChanged.connect(self.on_plot_cb_click)
|
||||
self.ui.multicolored_cb.stateChanged.connect(self.on_multicolored_cb_click)
|
||||
|
||||
# Editor Signal
|
||||
self.ui.editor_button.clicked.connect(self.app.object2editor)
|
||||
|
||||
# Properties
|
||||
self.ui.properties_button.toggled.connect(self.on_properties)
|
||||
self.calculations_finished.connect(self.update_area_chull)
|
||||
|
||||
self.ui.generate_cnc_button.clicked.connect(self.on_generatecnc_button_click)
|
||||
self.ui.paint_tool_button.clicked.connect(lambda: self.app.paint_tool.run(toggle=False))
|
||||
self.ui.generate_ncc_button.clicked.connect(lambda: self.app.ncclear_tool.run(toggle=False))
|
||||
|
@ -614,6 +624,20 @@ class GeometryObject(FlatCAMObj, Geometry):
|
|||
|
||||
self.ui.geo_tools_table.drag_drop_sig.connect(self.rebuild_ui)
|
||||
|
||||
def on_properties(self, state):
|
||||
if state:
|
||||
self.ui.properties_frame.show()
|
||||
else:
|
||||
self.ui.properties_frame.hide()
|
||||
return
|
||||
|
||||
self.ui.treeWidget.clear()
|
||||
self.add_properties_items(obj=self, treeWidget=self.ui.treeWidget)
|
||||
|
||||
self.ui.treeWidget.setSizePolicy(QtWidgets.QSizePolicy.Ignored, QtWidgets.QSizePolicy.MinimumExpanding)
|
||||
# make sure that the FCTree widget columns are resized to content
|
||||
self.ui.treeWidget.resize_sig.emit()
|
||||
|
||||
def rebuild_ui(self):
|
||||
# read the table tools uid
|
||||
current_uid_list = []
|
||||
|
@ -722,7 +746,13 @@ class GeometryObject(FlatCAMObj, Geometry):
|
|||
self.ui.plot_cb.stateChanged.connect(self.on_plot_cb_click)
|
||||
|
||||
# common parameters update
|
||||
self.ui.toolchangeg_cb.stateChanged.connect(self.update_common_param_in_storage)
|
||||
self.ui.toolchangez_entry.editingFinished.connect(self.update_common_param_in_storage)
|
||||
self.ui.endz_entry.editingFinished.connect(self.update_common_param_in_storage)
|
||||
self.ui.endxy_entry.editingFinished.connect(self.update_common_param_in_storage)
|
||||
self.ui.pp_geometry_name_cb.currentIndexChanged.connect(self.update_common_param_in_storage)
|
||||
self.ui.exclusion_cb.stateChanged.connect(self.update_common_param_in_storage)
|
||||
self.ui.polish_cb.stateChanged.connect(self.update_common_param_in_storage)
|
||||
|
||||
def ui_disconnect(self):
|
||||
|
||||
|
@ -806,6 +836,36 @@ class GeometryObject(FlatCAMObj, Geometry):
|
|||
except (TypeError, AttributeError):
|
||||
pass
|
||||
|
||||
# common parameters update
|
||||
try:
|
||||
self.ui.toolchangeg_cb.stateChanged.disconnect(self.update_common_param_in_storage)
|
||||
except (TypeError, AttributeError):
|
||||
pass
|
||||
try:
|
||||
self.ui.toolchangez_entry.editingFinished.disconnect(self.update_common_param_in_storage)
|
||||
except (TypeError, AttributeError):
|
||||
pass
|
||||
try:
|
||||
self.ui.endz_entry.editingFinished.disconnect(self.update_common_param_in_storage)
|
||||
except (TypeError, AttributeError):
|
||||
pass
|
||||
try:
|
||||
self.ui.endxy_entry.editingFinished.disconnect(self.update_common_param_in_storage)
|
||||
except (TypeError, AttributeError):
|
||||
pass
|
||||
try:
|
||||
self.ui.pp_geometry_name_cb.currentIndexChanged.disconnect(self.update_common_param_in_storage)
|
||||
except (TypeError, AttributeError):
|
||||
pass
|
||||
try:
|
||||
self.ui.exclusion_cb.stateChanged.disconnect(self.update_common_param_in_storage)
|
||||
except (TypeError, AttributeError):
|
||||
pass
|
||||
try:
|
||||
self.ui.polish_cb.stateChanged.disconnect(self.update_common_param_in_storage)
|
||||
except (TypeError, AttributeError):
|
||||
pass
|
||||
|
||||
def on_toggle_all_rows(self):
|
||||
"""
|
||||
will toggle the selection of all rows in Tools table
|
||||
|
@ -1555,7 +1615,13 @@ class GeometryObject(FlatCAMObj, Geometry):
|
|||
|
||||
def update_common_param_in_storage(self):
|
||||
for tooluid_value in self.tools.values():
|
||||
tooluid_value['data']['toolchange'] = self.ui.toolchangeg_cb.get_value()
|
||||
tooluid_value['data']['toolchangez'] = self.ui.toolchangez_entry.get_value()
|
||||
tooluid_value['data']['endz'] = self.ui.endz_entry.get_value()
|
||||
tooluid_value['data']['endxy'] = self.ui.endxy_entry.get_value()
|
||||
tooluid_value['data']['ppname_g'] = self.ui.pp_geometry_name_cb.get_value()
|
||||
tooluid_value['data']['area_exclusion'] = self.ui.exclusion_cb.get_value()
|
||||
tooluid_value['data']['polish'] = self.ui.polish_cb.get_value()
|
||||
|
||||
def select_tools_table_row(self, row, clearsel=None):
|
||||
if clearsel:
|
||||
|
@ -1854,6 +1920,7 @@ class GeometryObject(FlatCAMObj, Geometry):
|
|||
return
|
||||
|
||||
self.multigeo = True
|
||||
|
||||
# Object initialization function for app.app_obj.new_object()
|
||||
# RUNNING ON SEPARATE THREAD!
|
||||
def job_init_single_geometry(job_obj, app_obj):
|
||||
|
@ -1985,7 +2052,7 @@ class GeometryObject(FlatCAMObj, Geometry):
|
|||
# TODO this serve for bounding box creation only; should be optimized
|
||||
# commented this; there is no need for the actual GCode geometry - the original one will serve as well
|
||||
# for bounding box values
|
||||
# dia_cnc_dict['solid_geometry'] = cascaded_union([geo['geom'] for geo in dia_cnc_dict['gcode_parsed']])
|
||||
# dia_cnc_dict['solid_geometry'] = unary_union([geo['geom'] for geo in dia_cnc_dict['gcode_parsed']])
|
||||
try:
|
||||
dia_cnc_dict['solid_geometry'] = tool_solid_geometry
|
||||
self.app.inform.emit('[success] %s...' % _("Finished G-Code processing"))
|
||||
|
@ -2115,9 +2182,9 @@ class GeometryObject(FlatCAMObj, Geometry):
|
|||
is_first = True if tooluid_key == tool_lst[0] else False
|
||||
is_last = True if tooluid_key == tool_lst[-1] else False
|
||||
res, start_gcode = job_obj.geometry_tool_gcode_gen(tooluid_key, tools_dict, first_pt=(0, 0),
|
||||
tolerance = tol,
|
||||
tolerance=tol,
|
||||
is_first=is_first, is_last=is_last,
|
||||
toolchange = True)
|
||||
toolchange=True)
|
||||
if res == 'fail':
|
||||
log.debug("GeometryObject.mtool_gen_cncjob() --> generate_from_geometry2() failed")
|
||||
return 'fail'
|
||||
|
@ -2135,7 +2202,7 @@ class GeometryObject(FlatCAMObj, Geometry):
|
|||
# TODO this serve for bounding box creation only; should be optimized
|
||||
# commented this; there is no need for the actual GCode geometry - the original one will serve as well
|
||||
# for bounding box values
|
||||
# geo_for_bound_values = cascaded_union([
|
||||
# geo_for_bound_values = unary_union([
|
||||
# geo['geom'] for geo in dia_cnc_dict['gcode_parsed'] if geo['geom'].is_valid is True
|
||||
# ])
|
||||
try:
|
||||
|
@ -2303,8 +2370,8 @@ class GeometryObject(FlatCAMObj, Geometry):
|
|||
toolchangexy=toolchangexy,
|
||||
extracut=extracut, extracut_length=extracut_length,
|
||||
startz=startz, endz=endz, endxy=endxy,
|
||||
pp_geometry_name=ppname_g
|
||||
)
|
||||
pp_geometry_name=ppname_g)
|
||||
|
||||
job_obj.source_file = res
|
||||
# tell gcode_parse from which point to start drawing the lines depending on what kind of object is the
|
||||
# source of gcode
|
||||
|
@ -2872,13 +2939,13 @@ class GeometryObject(FlatCAMObj, Geometry):
|
|||
self.plot()
|
||||
|
||||
@staticmethod
|
||||
def merge(geo_list, geo_final, multigeo=None, fuse_tools=None):
|
||||
def merge(geo_list, geo_final, multi_geo=None, fuse_tools=None):
|
||||
"""
|
||||
Merges the geometry of objects in grb_list into the geometry of geo_final.
|
||||
|
||||
:param geo_list: List of GerberObject Objects to join.
|
||||
:param geo_final: Destination GerberObject object.
|
||||
:param multigeo: if the merged geometry objects are of type MultiGeo
|
||||
:param multi_geo: if the merged geometry objects are of type MultiGeo
|
||||
:param fuse_tools: If True will try to fuse tools of the same type for the Geometry objects
|
||||
:return: None
|
||||
"""
|
||||
|
@ -2908,7 +2975,7 @@ class GeometryObject(FlatCAMObj, Geometry):
|
|||
GeometryObject.merge(geo_list=geo_obj, geo_final=geo_final)
|
||||
# If not list, just append
|
||||
else:
|
||||
if multigeo is None or multigeo is False:
|
||||
if multi_geo is None or multi_geo is False:
|
||||
geo_final.multigeo = False
|
||||
else:
|
||||
geo_final.multigeo = True
|
||||
|
@ -2966,19 +3033,23 @@ class GeometryObject(FlatCAMObj, Geometry):
|
|||
new_tool_nr = 1
|
||||
for i_lst in intersect_list:
|
||||
new_solid_geo = []
|
||||
last_tool = None
|
||||
for old_tool in i_lst:
|
||||
new_solid_geo += new_tools[old_tool]['solid_geometry']
|
||||
last_tool = old_tool
|
||||
|
||||
if new_solid_geo:
|
||||
if new_solid_geo and last_tool:
|
||||
final_tools[new_tool_nr] = \
|
||||
{
|
||||
k: deepcopy(new_tools[old_tool][k]) for k in new_tools[old_tool] if k != 'solid_geometry'
|
||||
k: deepcopy(new_tools[last_tool][k]) for k in new_tools[last_tool] if k != 'solid_geometry'
|
||||
}
|
||||
final_tools[new_tool_nr]['solid_geometry'] = deepcopy(new_solid_geo)
|
||||
new_tool_nr += 1
|
||||
else:
|
||||
final_tools = new_tools
|
||||
|
||||
# if not final_tools:
|
||||
# return 'fail'
|
||||
geo_final.tools = final_tools
|
||||
|
||||
@staticmethod
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
|
||||
|
||||
from shapely.geometry import Point, Polygon, MultiPolygon, MultiLineString, LineString, LinearRing
|
||||
from shapely.ops import cascaded_union
|
||||
from shapely.ops import unary_union
|
||||
|
||||
from appParsers.ParseGerber import Gerber
|
||||
from appObjects.FlatCAMObj import *
|
||||
|
@ -150,6 +150,10 @@ class GerberObject(FlatCAMObj, Gerber):
|
|||
# Editor
|
||||
self.ui.editor_button.clicked.connect(lambda: self.app.object2editor())
|
||||
|
||||
# Properties
|
||||
self.ui.properties_button.toggled.connect(self.on_properties)
|
||||
self.calculations_finished.connect(self.update_area_chull)
|
||||
|
||||
# Tools
|
||||
self.ui.iso_button.clicked.connect(self.app.isolation_tool.run)
|
||||
self.ui.generate_ncc_button.clicked.connect(self.app.ncclear_tool.run)
|
||||
|
@ -343,6 +347,19 @@ class GerberObject(FlatCAMObj, Gerber):
|
|||
|
||||
return new_geo
|
||||
|
||||
def on_properties(self, state):
|
||||
if state:
|
||||
self.ui.properties_frame.show()
|
||||
else:
|
||||
self.ui.properties_frame.hide()
|
||||
return
|
||||
|
||||
self.ui.treeWidget.clear()
|
||||
self.add_properties_items(obj=self, treeWidget=self.ui.treeWidget)
|
||||
|
||||
# make sure that the FCTree widget columns are resized to content
|
||||
self.ui.treeWidget.resize_sig.emit()
|
||||
|
||||
def on_generate_buffer(self):
|
||||
self.app.inform.emit('[WARNING_NOTCL] %s...' % _("Buffering solid geometry"))
|
||||
|
||||
|
@ -369,7 +386,7 @@ class GerberObject(FlatCAMObj, Gerber):
|
|||
try:
|
||||
self.solid_geometry = MultiPolygon(self.solid_geometry)
|
||||
except Exception:
|
||||
self.solid_geometry = cascaded_union(self.solid_geometry)
|
||||
self.solid_geometry = unary_union(self.solid_geometry)
|
||||
|
||||
bounding_box = self.solid_geometry.envelope.buffer(float(self.options["noncoppermargin"]))
|
||||
if not self.options["noncopperrounded"]:
|
||||
|
@ -395,7 +412,7 @@ class GerberObject(FlatCAMObj, Gerber):
|
|||
try:
|
||||
self.solid_geometry = MultiPolygon(self.solid_geometry)
|
||||
except Exception:
|
||||
self.solid_geometry = cascaded_union(self.solid_geometry)
|
||||
self.solid_geometry = unary_union(self.solid_geometry)
|
||||
|
||||
# Bounding box with rounded corners
|
||||
bounding_box = self.solid_geometry.envelope.buffer(float(self.options["bboxmargin"]))
|
||||
|
|
|
@ -18,7 +18,12 @@ from appCommon.Common import LoudDict
|
|||
from appGUI.PlotCanvasLegacy import ShapeCollectionLegacy
|
||||
from appGUI.VisPyVisuals import ShapeCollection
|
||||
|
||||
from shapely.ops import unary_union
|
||||
from shapely.geometry import Polygon, MultiPolygon
|
||||
|
||||
from copy import deepcopy
|
||||
import sys
|
||||
import math
|
||||
|
||||
import gettext
|
||||
import appTranslation as fcTranslate
|
||||
|
@ -55,6 +60,9 @@ class FlatCAMObj(QtCore.QObject):
|
|||
# signal to plot a single object
|
||||
plot_single_object = QtCore.pyqtSignal()
|
||||
|
||||
# signal for Properties
|
||||
calculations_finished = QtCore.pyqtSignal(float, float, float, float, float, object)
|
||||
|
||||
def __init__(self, name):
|
||||
"""
|
||||
Constructor.
|
||||
|
@ -112,6 +120,9 @@ class FlatCAMObj(QtCore.QObject):
|
|||
# self.units = 'IN'
|
||||
self.units = self.app.defaults['units']
|
||||
|
||||
# this is the treeWidget from the UI; it is updated when the add_properties_items() method is called
|
||||
self.treeWidget = None
|
||||
|
||||
self.plot_single_object.connect(self.single_object_plot)
|
||||
|
||||
def __del__(self):
|
||||
|
@ -456,6 +467,444 @@ class FlatCAMObj(QtCore.QObject):
|
|||
self.app.defaults[filter_string] = ';;'.join(filter_list)
|
||||
return
|
||||
|
||||
def add_properties_items(self, obj, treeWidget):
|
||||
self.treeWidget = treeWidget
|
||||
parent = self.treeWidget.invisibleRootItem()
|
||||
apertures = ''
|
||||
tools = ''
|
||||
drills = ''
|
||||
slots = ''
|
||||
others = ''
|
||||
|
||||
font = QtGui.QFont()
|
||||
font.setBold(True)
|
||||
|
||||
p_color = QtGui.QColor("#000000") if self.app.defaults['global_gray_icons'] is False \
|
||||
else QtGui.QColor("#FFFFFF")
|
||||
|
||||
# main Items categories
|
||||
dims = self.treeWidget.addParent(
|
||||
parent, _('Dimensions'), expanded=True, color=p_color, font=font)
|
||||
options = self.treeWidget.addParent(parent, _('Options'), color=p_color, font=font)
|
||||
|
||||
if obj.kind.lower() == 'gerber':
|
||||
apertures = self.treeWidget.addParent(
|
||||
parent, _('Apertures'), expanded=True, color=p_color, font=font)
|
||||
else:
|
||||
tools = self.treeWidget.addParent(
|
||||
parent, _('Tools'), expanded=True, color=p_color, font=font)
|
||||
|
||||
if obj.kind.lower() == 'excellon':
|
||||
drills = self.treeWidget.addParent(
|
||||
parent, _('Drills'), expanded=True, color=p_color, font=font)
|
||||
slots = self.treeWidget.addParent(
|
||||
parent, _('Slots'), expanded=True, color=p_color, font=font)
|
||||
|
||||
if obj.kind.lower() == 'cncjob':
|
||||
others = self.treeWidget.addParent(
|
||||
parent, _('Others'), expanded=True, color=p_color, font=font)
|
||||
|
||||
# separator = self.treeWidget.addParent(parent, '')
|
||||
|
||||
def job_thread(obj_prop):
|
||||
self.app.proc_container.new(_("Calculating dimensions ... Please wait."))
|
||||
|
||||
length = 0.0
|
||||
width = 0.0
|
||||
area = 0.0
|
||||
copper_area = 0.0
|
||||
|
||||
geo = obj_prop.solid_geometry
|
||||
if geo:
|
||||
# calculate physical dimensions
|
||||
try:
|
||||
xmin, ymin, xmax, ymax = obj_prop.bounds()
|
||||
|
||||
length = abs(xmax - xmin)
|
||||
width = abs(ymax - ymin)
|
||||
except Exception as ee:
|
||||
log.debug("FlatCAMObj.addItems() -> calculate dimensions --> %s" % str(ee))
|
||||
|
||||
# calculate box area
|
||||
if self.app.defaults['units'].lower() == 'mm':
|
||||
area = (length * width) / 100
|
||||
else:
|
||||
area = length * width
|
||||
|
||||
if obj_prop.kind.lower() == 'gerber':
|
||||
# calculate copper area
|
||||
try:
|
||||
for geo_el in geo:
|
||||
copper_area += geo_el.area
|
||||
except TypeError:
|
||||
copper_area += geo.area
|
||||
copper_area /= 100
|
||||
else:
|
||||
xmin = []
|
||||
ymin = []
|
||||
xmax = []
|
||||
ymax = []
|
||||
|
||||
if obj_prop.kind.lower() == 'cncjob':
|
||||
try:
|
||||
for tool_k in obj_prop.exc_cnc_tools:
|
||||
x0, y0, x1, y1 = unary_union(obj_prop.exc_cnc_tools[tool_k]['solid_geometry']).bounds
|
||||
xmin.append(x0)
|
||||
ymin.append(y0)
|
||||
xmax.append(x1)
|
||||
ymax.append(y1)
|
||||
except Exception as ee:
|
||||
log.debug("FlatCAMObj.addItems() --> %s" % str(ee))
|
||||
|
||||
try:
|
||||
for tool_k in obj_prop.cnc_tools:
|
||||
x0, y0, x1, y1 = unary_union(obj_prop.cnc_tools[tool_k]['solid_geometry']).bounds
|
||||
xmin.append(x0)
|
||||
ymin.append(y0)
|
||||
xmax.append(x1)
|
||||
ymax.append(y1)
|
||||
except Exception as ee:
|
||||
log.debug("FlatCAMObj.addItems() --> %s" % str(ee))
|
||||
else:
|
||||
try:
|
||||
for tool_k in obj_prop.tools:
|
||||
x0, y0, x1, y1 = unary_union(obj_prop.tools[tool_k]['solid_geometry']).bounds
|
||||
xmin.append(x0)
|
||||
ymin.append(y0)
|
||||
xmax.append(x1)
|
||||
ymax.append(y1)
|
||||
except Exception as ee:
|
||||
log.debug("FlatCAMObj.addItems() --> %s" % str(ee))
|
||||
|
||||
try:
|
||||
xmin = min(xmin)
|
||||
ymin = min(ymin)
|
||||
xmax = max(xmax)
|
||||
ymax = max(ymax)
|
||||
|
||||
length = abs(xmax - xmin)
|
||||
width = abs(ymax - ymin)
|
||||
|
||||
# calculate box area
|
||||
if self.app.defaults['units'].lower() == 'mm':
|
||||
area = (length * width) / 100
|
||||
else:
|
||||
area = length * width
|
||||
|
||||
if obj_prop.kind.lower() == 'gerber':
|
||||
# calculate copper area
|
||||
|
||||
# create a complete solid_geometry from the tools
|
||||
geo_tools = []
|
||||
for tool_k in obj_prop.tools:
|
||||
if 'solid_geometry' in obj_prop.tools[tool_k]:
|
||||
for geo_el in obj_prop.tools[tool_k]['solid_geometry']:
|
||||
geo_tools.append(geo_el)
|
||||
|
||||
try:
|
||||
for geo_el in geo_tools:
|
||||
copper_area += geo_el.area
|
||||
except TypeError:
|
||||
copper_area += geo_tools.area
|
||||
copper_area /= 100
|
||||
except Exception as err:
|
||||
log.debug("FlatCAMObj.addItems() --> %s" % str(err))
|
||||
|
||||
area_chull = 0.0
|
||||
if obj_prop.kind.lower() != 'cncjob':
|
||||
# calculate and add convex hull area
|
||||
if geo:
|
||||
if isinstance(geo, list) and geo[0] is not None:
|
||||
if isinstance(geo, MultiPolygon):
|
||||
env_obj = geo.convex_hull
|
||||
elif (isinstance(geo, MultiPolygon) and len(geo) == 1) or \
|
||||
(isinstance(geo, list) and len(geo) == 1) and isinstance(geo[0], Polygon):
|
||||
env_obj = unary_union(geo)
|
||||
env_obj = env_obj.convex_hull
|
||||
else:
|
||||
env_obj = unary_union(geo)
|
||||
env_obj = env_obj.convex_hull
|
||||
|
||||
area_chull = env_obj.area
|
||||
else:
|
||||
area_chull = 0
|
||||
else:
|
||||
try:
|
||||
area_chull = []
|
||||
for tool_k in obj_prop.tools:
|
||||
area_el = unary_union(obj_prop.tools[tool_k]['solid_geometry']).convex_hull
|
||||
area_chull.append(area_el.area)
|
||||
area_chull = max(area_chull)
|
||||
except Exception as er:
|
||||
area_chull = None
|
||||
log.debug("FlatCAMObj.addItems() --> %s" % str(er))
|
||||
|
||||
if self.app.defaults['units'].lower() == 'mm' and area_chull:
|
||||
area_chull = area_chull / 100
|
||||
|
||||
if area_chull is None:
|
||||
area_chull = 0
|
||||
|
||||
self.calculations_finished.emit(area, length, width, area_chull, copper_area, dims)
|
||||
|
||||
self.app.worker_task.emit({'fcn': job_thread, 'params': [obj]})
|
||||
|
||||
# Options items
|
||||
for option in obj.options:
|
||||
if option == 'name':
|
||||
continue
|
||||
self.treeWidget.addChild(options, [str(option), str(obj.options[option])], True)
|
||||
|
||||
# Items that depend on the object type
|
||||
if obj.kind.lower() == 'gerber':
|
||||
temp_ap = {}
|
||||
for ap in obj.apertures:
|
||||
temp_ap.clear()
|
||||
temp_ap = deepcopy(obj.apertures[ap])
|
||||
temp_ap.pop('geometry', None)
|
||||
|
||||
solid_nr = 0
|
||||
follow_nr = 0
|
||||
clear_nr = 0
|
||||
|
||||
if 'geometry' in obj.apertures[ap]:
|
||||
if obj.apertures[ap]['geometry']:
|
||||
font.setBold(True)
|
||||
for el in obj.apertures[ap]['geometry']:
|
||||
if 'solid' in el:
|
||||
solid_nr += 1
|
||||
if 'follow' in el:
|
||||
follow_nr += 1
|
||||
if 'clear' in el:
|
||||
clear_nr += 1
|
||||
else:
|
||||
font.setBold(False)
|
||||
temp_ap['Solid_Geo'] = '%s Polygons' % str(solid_nr)
|
||||
temp_ap['Follow_Geo'] = '%s LineStrings' % str(follow_nr)
|
||||
temp_ap['Clear_Geo'] = '%s Polygons' % str(clear_nr)
|
||||
|
||||
apid = self.treeWidget.addParent(
|
||||
apertures, str(ap), expanded=False, color=p_color, font=font)
|
||||
for key in temp_ap:
|
||||
self.treeWidget.addChild(apid, [str(key), str(temp_ap[key])], True)
|
||||
elif obj.kind.lower() == 'excellon':
|
||||
tot_drill_cnt = 0
|
||||
tot_slot_cnt = 0
|
||||
|
||||
for tool, value in obj.tools.items():
|
||||
toolid = self.treeWidget.addParent(
|
||||
tools, str(tool), expanded=False, color=p_color, font=font)
|
||||
|
||||
drill_cnt = 0 # variable to store the nr of drills per tool
|
||||
slot_cnt = 0 # variable to store the nr of slots per tool
|
||||
|
||||
# Find no of drills for the current tool
|
||||
if 'drills' in value and value['drills']:
|
||||
drill_cnt = len(value['drills'])
|
||||
|
||||
tot_drill_cnt += drill_cnt
|
||||
|
||||
# Find no of slots for the current tool
|
||||
if 'slots' in value and value['slots']:
|
||||
slot_cnt = len(value['slots'])
|
||||
|
||||
tot_slot_cnt += slot_cnt
|
||||
|
||||
self.treeWidget.addChild(
|
||||
toolid,
|
||||
[
|
||||
_('Diameter'),
|
||||
'%.*f %s' % (self.decimals, value['tooldia'], self.app.defaults['units'].lower())
|
||||
],
|
||||
True
|
||||
)
|
||||
self.treeWidget.addChild(toolid, [_('Drills number'), str(drill_cnt)], True)
|
||||
self.treeWidget.addChild(toolid, [_('Slots number'), str(slot_cnt)], True)
|
||||
|
||||
self.treeWidget.addChild(drills, [_('Drills total number:'), str(tot_drill_cnt)], True)
|
||||
self.treeWidget.addChild(slots, [_('Slots total number:'), str(tot_slot_cnt)], True)
|
||||
elif obj.kind.lower() == 'geometry':
|
||||
for tool, value in obj.tools.items():
|
||||
geo_tool = self.treeWidget.addParent(
|
||||
tools, str(tool), expanded=False, color=p_color, font=font)
|
||||
for k, v in value.items():
|
||||
if k == 'solid_geometry':
|
||||
# printed_value = _('Present') if v else _('None')
|
||||
try:
|
||||
printed_value = str(len(v))
|
||||
except (TypeError, AttributeError):
|
||||
printed_value = '1'
|
||||
self.treeWidget.addChild(geo_tool, [str(k), printed_value], True)
|
||||
elif k == 'data':
|
||||
tool_data = self.treeWidget.addParent(
|
||||
geo_tool, str(k).capitalize(), color=p_color, font=font)
|
||||
for data_k, data_v in v.items():
|
||||
self.treeWidget.addChild(tool_data, [str(data_k), str(data_v)], True)
|
||||
else:
|
||||
self.treeWidget.addChild(geo_tool, [str(k), str(v)], True)
|
||||
elif obj.kind.lower() == 'cncjob':
|
||||
# for cncjob objects made from gerber or geometry
|
||||
for tool, value in obj.cnc_tools.items():
|
||||
geo_tool = self.treeWidget.addParent(
|
||||
tools, str(tool), expanded=False, color=p_color, font=font)
|
||||
for k, v in value.items():
|
||||
if k == 'solid_geometry':
|
||||
printed_value = _('Present') if v else _('None')
|
||||
self.treeWidget.addChild(geo_tool, [_("Solid Geometry"), printed_value], True)
|
||||
elif k == 'gcode':
|
||||
printed_value = _('Present') if v != '' else _('None')
|
||||
self.treeWidget.addChild(geo_tool, [_("GCode Text"), printed_value], True)
|
||||
elif k == 'gcode_parsed':
|
||||
printed_value = _('Present') if v else _('None')
|
||||
self.treeWidget.addChild(geo_tool, [_("GCode Geometry"), printed_value], True)
|
||||
elif k == 'data':
|
||||
pass
|
||||
else:
|
||||
self.treeWidget.addChild(geo_tool, [str(k), str(v)], True)
|
||||
|
||||
v = value['data']
|
||||
tool_data = self.treeWidget.addParent(
|
||||
geo_tool, _("Tool Data"), color=p_color, font=font)
|
||||
for data_k, data_v in v.items():
|
||||
self.treeWidget.addChild(tool_data, [str(data_k).capitalize(), str(data_v)], True)
|
||||
|
||||
# for cncjob objects made from excellon
|
||||
for tool_dia, value in obj.exc_cnc_tools.items():
|
||||
exc_tool = self.treeWidget.addParent(
|
||||
tools, str(value['tool']), expanded=False, color=p_color, font=font
|
||||
)
|
||||
self.treeWidget.addChild(
|
||||
exc_tool,
|
||||
[
|
||||
_('Diameter'),
|
||||
'%.*f %s' % (self.decimals, tool_dia, self.app.defaults['units'].lower())
|
||||
],
|
||||
True
|
||||
)
|
||||
for k, v in value.items():
|
||||
if k == 'solid_geometry':
|
||||
printed_value = _('Present') if v else _('None')
|
||||
self.treeWidget.addChild(exc_tool, [_("Solid Geometry"), printed_value], True)
|
||||
elif k == 'nr_drills':
|
||||
self.treeWidget.addChild(exc_tool, [_("Drills number"), str(v)], True)
|
||||
elif k == 'nr_slots':
|
||||
self.treeWidget.addChild(exc_tool, [_("Slots number"), str(v)], True)
|
||||
elif k == 'gcode':
|
||||
printed_value = _('Present') if v != '' else _('None')
|
||||
self.treeWidget.addChild(exc_tool, [_("GCode Text"), printed_value], True)
|
||||
elif k == 'gcode_parsed':
|
||||
printed_value = _('Present') if v else _('None')
|
||||
self.treeWidget.addChild(exc_tool, [_("GCode Geometry"), printed_value], True)
|
||||
else:
|
||||
pass
|
||||
|
||||
self.treeWidget.addChild(
|
||||
exc_tool,
|
||||
[
|
||||
_("Depth of Cut"),
|
||||
'%.*f %s' % (
|
||||
self.decimals,
|
||||
(obj.z_cut - abs(value['data']['tools_drill_offset'])),
|
||||
self.app.defaults['units'].lower()
|
||||
)
|
||||
],
|
||||
True
|
||||
)
|
||||
self.treeWidget.addChild(
|
||||
exc_tool,
|
||||
[
|
||||
_("Clearance Height"),
|
||||
'%.*f %s' % (
|
||||
self.decimals,
|
||||
obj.z_move,
|
||||
self.app.defaults['units'].lower()
|
||||
)
|
||||
],
|
||||
True
|
||||
)
|
||||
self.treeWidget.addChild(
|
||||
exc_tool,
|
||||
[
|
||||
_("Feedrate"),
|
||||
'%.*f %s/min' % (
|
||||
self.decimals,
|
||||
obj.feedrate,
|
||||
self.app.defaults['units'].lower()
|
||||
)
|
||||
],
|
||||
True
|
||||
)
|
||||
|
||||
v = value['data']
|
||||
tool_data = self.treeWidget.addParent(
|
||||
exc_tool, _("Tool Data"), color=p_color, font=font)
|
||||
for data_k, data_v in v.items():
|
||||
self.treeWidget.addChild(tool_data, [str(data_k).capitalize(), str(data_v)], True)
|
||||
|
||||
r_time = obj.routing_time
|
||||
if r_time > 1:
|
||||
units_lbl = 'min'
|
||||
else:
|
||||
r_time *= 60
|
||||
units_lbl = 'sec'
|
||||
r_time = math.ceil(float(r_time))
|
||||
self.treeWidget.addChild(
|
||||
others,
|
||||
[
|
||||
'%s:' % _('Routing time'),
|
||||
'%.*f %s' % (self.decimals, r_time, units_lbl)],
|
||||
True
|
||||
)
|
||||
self.treeWidget.addChild(
|
||||
others,
|
||||
[
|
||||
'%s:' % _('Travelled distance'),
|
||||
'%.*f %s' % (self.decimals, obj.travel_distance, self.app.defaults['units'].lower())
|
||||
],
|
||||
True
|
||||
)
|
||||
|
||||
# treeWidget.addChild(separator, [''])
|
||||
|
||||
def update_area_chull(self, area, length, width, chull_area, copper_area, location):
|
||||
|
||||
# add dimensions
|
||||
self.treeWidget.addChild(
|
||||
location,
|
||||
['%s:' % _('Length'), '%.*f %s' % (self.decimals, length, self.app.defaults['units'].lower())],
|
||||
True
|
||||
)
|
||||
self.treeWidget.addChild(
|
||||
location,
|
||||
['%s:' % _('Width'), '%.*f %s' % (self.decimals, width, self.app.defaults['units'].lower())],
|
||||
True
|
||||
)
|
||||
|
||||
# add box area
|
||||
if self.app.defaults['units'].lower() == 'mm':
|
||||
self.treeWidget.addChild(location, ['%s:' % _('Box Area'), '%.*f %s' % (self.decimals, area, 'cm2')], True)
|
||||
self.treeWidget.addChild(
|
||||
location,
|
||||
['%s:' % _('Convex_Hull Area'), '%.*f %s' % (self.decimals, chull_area, 'cm2')],
|
||||
True
|
||||
)
|
||||
|
||||
else:
|
||||
self.treeWidget.addChild(location, ['%s:' % _('Box Area'), '%.*f %s' % (self.decimals, area, 'in2')], True)
|
||||
self.treeWidget.addChild(
|
||||
location,
|
||||
['%s:' % _('Convex_Hull Area'), '%.*f %s' % (self.decimals, chull_area, 'in2')],
|
||||
True
|
||||
)
|
||||
|
||||
# add copper area
|
||||
if self.app.defaults['units'].lower() == 'mm':
|
||||
self.treeWidget.addChild(
|
||||
location, ['%s:' % _('Copper Area'), '%.*f %s' % (self.decimals, copper_area, 'cm2')], True)
|
||||
else:
|
||||
self.treeWidget.addChild(
|
||||
location, ['%s:' % _('Copper Area'), '%.*f %s' % (self.decimals, copper_area, 'in2')], True)
|
||||
|
||||
@staticmethod
|
||||
def poly2rings(poly):
|
||||
return [poly.exterior] + [interior for interior in poly.interiors]
|
||||
|
@ -471,8 +920,8 @@ class FlatCAMObj(QtCore.QObject):
|
|||
current_visibility = self.shapes.visible
|
||||
# self.shapes.visible = value # maybe this is slower in VisPy? use enabled property?
|
||||
|
||||
def task(current_visibility):
|
||||
if current_visibility is True:
|
||||
def task(visibility):
|
||||
if visibility is True:
|
||||
if value is False:
|
||||
self.shapes.visible = False
|
||||
else:
|
||||
|
@ -517,4 +966,4 @@ class FlatCAMObj(QtCore.QObject):
|
|||
del self.options
|
||||
|
||||
# Set flag
|
||||
self.deleted = True
|
||||
self.deleted = True
|
||||
|
|
|
@ -971,7 +971,7 @@ class ObjectCollection(QtCore.QAbstractItemModel):
|
|||
except Exception as e:
|
||||
log.debug("Nothing to remove. %s" % str(e))
|
||||
|
||||
self.app.setup_component_editor()
|
||||
self.app.setup_default_properties_tab()
|
||||
return
|
||||
|
||||
if obj:
|
||||
|
|
|
@ -374,7 +374,7 @@ class Gerber(Geometry):
|
|||
geo_f = None
|
||||
|
||||
# Polygons are stored here until there is a change in polarity.
|
||||
# Only then they are combined via cascaded_union and added or
|
||||
# Only then they are combined via unary_union and added or
|
||||
# subtracted from solid_geometry. This is ~100 times faster than
|
||||
# applying a union for every new polygon.
|
||||
poly_buffer = []
|
||||
|
@ -1680,7 +1680,7 @@ class Gerber(Geometry):
|
|||
#
|
||||
# self.do_flashes()
|
||||
#
|
||||
# self.solid_geometry = cascaded_union(self.buffered_paths +
|
||||
# self.solid_geometry = unary_union(self.buffered_paths +
|
||||
# [poly['polygon'] for poly in self.regions] +
|
||||
# self.flash_geometry)
|
||||
|
||||
|
|
|
@ -228,8 +228,10 @@ def svgrect2shapely(rect, n_points=32, factor=1.0):
|
|||
else:
|
||||
y = 0
|
||||
|
||||
rxstr = rect.get('rx') * factor
|
||||
rystr = rect.get('ry') * factor
|
||||
rxstr = rect.get('rx')
|
||||
rxstr = rxstr * factor if rxstr else rxstr
|
||||
rystr = rect.get('ry')
|
||||
rystr = rystr * factor if rystr else rystr
|
||||
|
||||
if rxstr is None and rystr is None: # Sharp corners
|
||||
pts = [
|
||||
|
@ -290,9 +292,12 @@ def svgcircle2shapely(circle, n_points=64, factor=1.0):
|
|||
# cx = float(circle.get('cx'))
|
||||
# cy = float(circle.get('cy'))
|
||||
# r = float(circle.get('r'))
|
||||
cx = svgparselength(circle.get('cx'))[0] * factor # TODO: No units support yet
|
||||
cy = svgparselength(circle.get('cy'))[0] * factor # TODO: No units support yet
|
||||
r = svgparselength(circle.get('r'))[0] * factor # TODO: No units support yet
|
||||
cx = svgparselength(circle.get('cx'))[0] # TODO: No units support yet
|
||||
cx = cx * factor if cx else cx
|
||||
cy = svgparselength(circle.get('cy'))[0] # TODO: No units support yet
|
||||
cy = cy * factor if cy else cy
|
||||
r = svgparselength(circle.get('r'))[0] # TODO: No units support yet
|
||||
r = r * factor if r else r
|
||||
|
||||
return Point(cx, cy).buffer(r, resolution=n_points)
|
||||
|
||||
|
@ -309,11 +314,15 @@ def svgellipse2shapely(ellipse, n_points=64, factor=1.0):
|
|||
:rtype: shapely.geometry.polygon.LinearRing
|
||||
"""
|
||||
|
||||
cx = svgparselength(ellipse.get('cx'))[0] * factor # TODO: No units support yet
|
||||
cy = svgparselength(ellipse.get('cy'))[0] * factor # TODO: No units support yet
|
||||
cx = svgparselength(ellipse.get('cx'))[0] # TODO: No units support yet
|
||||
cx = cx * factor if cx else cx
|
||||
cy = svgparselength(ellipse.get('cy'))[0] # TODO: No units support yet
|
||||
cy = cy * factor if cy else cy
|
||||
|
||||
rx = svgparselength(ellipse.get('rx'))[0] * factor # TODO: No units support yet
|
||||
ry = svgparselength(ellipse.get('ry'))[0] * factor # TODO: No units support yet
|
||||
rx = svgparselength(ellipse.get('rx'))[0] # TODO: No units support yet
|
||||
rx = rx * factor if rx else rx
|
||||
ry = svgparselength(ellipse.get('ry'))[0] # TODO: No units support yet
|
||||
ry = ry * factor if ry else ry
|
||||
|
||||
t = np.arange(n_points, dtype=float) / n_points
|
||||
x = cx + rx * np.cos(2 * np.pi * t)
|
||||
|
|
|
@ -12,7 +12,7 @@ from appTool import AppTool
|
|||
from appGUI.GUIElements import FCDoubleSpinner, RadioSet, FCEntry, FCComboBox
|
||||
|
||||
import shapely.geometry.base as base
|
||||
from shapely.ops import cascaded_union, unary_union
|
||||
from shapely.ops import unary_union
|
||||
from shapely.geometry import Polygon, MultiPolygon, Point, LineString
|
||||
from shapely.geometry import box as box
|
||||
import shapely.affinity as affinity
|
||||
|
@ -428,7 +428,7 @@ class ToolCopperThieving(AppTool):
|
|||
if len(self.sel_rect) == 0:
|
||||
return
|
||||
|
||||
self.sel_rect = cascaded_union(self.sel_rect)
|
||||
self.sel_rect = unary_union(self.sel_rect)
|
||||
|
||||
if not isinstance(self.sel_rect, Iterable):
|
||||
self.sel_rect = [self.sel_rect]
|
||||
|
@ -606,9 +606,9 @@ class ToolCopperThieving(AppTool):
|
|||
env_obj = geo_n.convex_hull
|
||||
elif (isinstance(geo_n, MultiPolygon) and len(geo_n) == 1) or \
|
||||
(isinstance(geo_n, list) and len(geo_n) == 1) and isinstance(geo_n[0], Polygon):
|
||||
env_obj = cascaded_union(geo_n)
|
||||
env_obj = unary_union(geo_n)
|
||||
else:
|
||||
env_obj = cascaded_union(geo_n)
|
||||
env_obj = unary_union(geo_n)
|
||||
env_obj = env_obj.convex_hull
|
||||
bounding_box = env_obj.buffer(distance=margin, join_style=base.JOIN_STYLE.mitre)
|
||||
else:
|
||||
|
@ -660,10 +660,10 @@ class ToolCopperThieving(AppTool):
|
|||
raise grace
|
||||
geo_buff_list.append(poly.buffer(distance=margin, join_style=base.JOIN_STYLE.mitre))
|
||||
|
||||
bounding_box = cascaded_union(geo_buff_list)
|
||||
bounding_box = unary_union(geo_buff_list)
|
||||
elif working_obj.kind == 'gerber':
|
||||
geo_n = cascaded_union(geo_n).convex_hull
|
||||
bounding_box = cascaded_union(thieving_obj.solid_geometry).convex_hull.intersection(geo_n)
|
||||
geo_n = unary_union(geo_n).convex_hull
|
||||
bounding_box = unary_union(thieving_obj.solid_geometry).convex_hull.intersection(geo_n)
|
||||
bounding_box = bounding_box.buffer(distance=margin, join_style=base.JOIN_STYLE.mitre)
|
||||
else:
|
||||
app_obj.app.inform.emit('[ERROR_NOTCL] %s' % _("The reference object type is not supported."))
|
||||
|
|
|
@ -11,7 +11,7 @@ from appGUI.GUIElements import FCDoubleSpinner, FCCheckBox, RadioSet, FCComboBox
|
|||
FCLabel
|
||||
|
||||
from shapely.geometry import box, MultiPolygon, Polygon, LineString, LinearRing, MultiLineString
|
||||
from shapely.ops import cascaded_union, unary_union, linemerge
|
||||
from shapely.ops import unary_union, linemerge
|
||||
import shapely.affinity as affinity
|
||||
|
||||
from matplotlib.backend_bases import KeyEvent as mpl_key_event
|
||||
|
@ -217,6 +217,7 @@ class CutOut(AppTool):
|
|||
"toolchangez": float(self.app.defaults["geometry_toolchangez"]),
|
||||
"startz": self.app.defaults["geometry_startz"],
|
||||
"endz": float(self.app.defaults["geometry_endz"]),
|
||||
"endxy": self.app.defaults["geometry_endxy"],
|
||||
"area_exclusion": self.app.defaults["geometry_area_exclusion"],
|
||||
"area_shape": self.app.defaults["geometry_area_shape"],
|
||||
"area_strategy": self.app.defaults["geometry_area_strategy"],
|
||||
|
@ -414,6 +415,7 @@ class CutOut(AppTool):
|
|||
"toolchangez": float(self.app.defaults["geometry_toolchangez"]),
|
||||
"startz": self.app.defaults["geometry_startz"],
|
||||
"endz": float(self.app.defaults["geometry_endz"]),
|
||||
"endxy": self.app.defaults["geometry_endxy"],
|
||||
"area_exclusion": self.app.defaults["geometry_area_exclusion"],
|
||||
"area_shape": self.app.defaults["geometry_area_shape"],
|
||||
"area_strategy": self.app.defaults["geometry_area_strategy"],
|
||||
|
@ -1832,7 +1834,7 @@ class CutOut(AppTool):
|
|||
log.debug("%d paths" % len(flat_geometry))
|
||||
|
||||
polygon = Polygon(points)
|
||||
toolgeo = cascaded_union(polygon)
|
||||
toolgeo = unary_union(polygon)
|
||||
diffs = []
|
||||
for target in flat_geometry:
|
||||
if type(target) == LineString or type(target) == LinearRing:
|
||||
|
@ -1906,7 +1908,7 @@ class CutOut(AppTool):
|
|||
|
||||
:param target_geo: geometry from which to subtract
|
||||
:param subtractor: a list of Points, a LinearRing or a Polygon that will be subtracted from target_geo
|
||||
:return: a cascaded union of the resulting geometry
|
||||
:return: a unary_union of the resulting geometry
|
||||
"""
|
||||
|
||||
if target_geo is None:
|
||||
|
@ -2082,8 +2084,9 @@ class CutoutUI:
|
|||
self.addtool_from_db_btn.setIcon(QtGui.QIcon(self.app.resource_location + '/search_db32.png'))
|
||||
self.addtool_from_db_btn.setToolTip(
|
||||
_("Add a new tool to the Tool Table\n"
|
||||
"from the Tool Database.\n"
|
||||
"Tool database administration in Menu: Options -> Tools Database")
|
||||
"from the Tools Database.\n"
|
||||
"Tools database administration in in:\n"
|
||||
"Menu: Options -> Tools Database")
|
||||
)
|
||||
hlay.addWidget(self.addtool_from_db_btn)
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@ from appGUI.GUIElements import FCEntry
|
|||
|
||||
from shapely.ops import nearest_points
|
||||
from shapely.geometry import Point, MultiPolygon
|
||||
from shapely.ops import cascaded_union
|
||||
from shapely.ops import unary_union
|
||||
|
||||
import math
|
||||
import logging
|
||||
|
@ -113,12 +113,12 @@ class DistanceMin(AppTool):
|
|||
try:
|
||||
selected_objs[0].solid_geometry = MultiPolygon(selected_objs[0].solid_geometry)
|
||||
except Exception:
|
||||
selected_objs[0].solid_geometry = cascaded_union(selected_objs[0].solid_geometry)
|
||||
selected_objs[0].solid_geometry = unary_union(selected_objs[0].solid_geometry)
|
||||
|
||||
try:
|
||||
selected_objs[1].solid_geometry = MultiPolygon(selected_objs[1].solid_geometry)
|
||||
except Exception:
|
||||
selected_objs[1].solid_geometry = cascaded_union(selected_objs[1].solid_geometry)
|
||||
selected_objs[1].solid_geometry = unary_union(selected_objs[1].solid_geometry)
|
||||
|
||||
first_pos, last_pos = nearest_points(selected_objs[0].solid_geometry, selected_objs[1].solid_geometry)
|
||||
|
||||
|
|
|
@ -9,11 +9,13 @@ from PyQt5 import QtCore, QtWidgets, QtGui
|
|||
|
||||
from appTool import AppTool
|
||||
from appGUI.GUIElements import RadioSet, FCDoubleSpinner, FCCheckBox, \
|
||||
OptionalHideInputSection, FCComboBox, FCFileSaveDialog, FCButton, FCLabel
|
||||
OptionalHideInputSection, FCComboBox, FCFileSaveDialog, FCButton, FCLabel, FCSpinner
|
||||
|
||||
from copy import deepcopy
|
||||
import logging
|
||||
from shapely.geometry import Polygon, MultiPolygon, Point
|
||||
import shapely.affinity as affinity
|
||||
from shapely.ops import unary_union
|
||||
|
||||
from reportlab.graphics import renderPDF
|
||||
from reportlab.pdfgen import canvas
|
||||
|
@ -138,6 +140,8 @@ class Film(AppTool):
|
|||
self.ui.orientation_radio.set_value(self.app.defaults["tools_film_orientation"])
|
||||
self.ui.pagesize_combo.set_value(self.app.defaults["tools_film_pagesize"])
|
||||
|
||||
self.ui.png_dpi_spinner.set_value(self.app.defaults["tools_film_png_dpi"])
|
||||
|
||||
self.ui.tf_type_obj_combo.set_value('grb')
|
||||
self.ui.tf_type_box_combo.set_value('grb')
|
||||
# run once to update the obj_type attribute in the FCCombobox so the last object is showed in cb
|
||||
|
@ -187,8 +191,8 @@ class Film(AppTool):
|
|||
def generate_positive_normal_film(self, name, boxname, factor, ftype='svg'):
|
||||
log.debug("ToolFilm.Film.generate_positive_normal_film() started ...")
|
||||
|
||||
scale_factor_x = None
|
||||
scale_factor_y = None
|
||||
scale_factor_x = 1
|
||||
scale_factor_y = 1
|
||||
skew_factor_x = None
|
||||
skew_factor_y = None
|
||||
mirror = None
|
||||
|
@ -328,8 +332,8 @@ class Film(AppTool):
|
|||
def generate_negative_film(self, name, boxname, factor, ftype='svg'):
|
||||
log.debug("ToolFilm.Film.generate_negative_film() started ...")
|
||||
|
||||
scale_factor_x = None
|
||||
scale_factor_y = None
|
||||
scale_factor_x = 1
|
||||
scale_factor_y = 1
|
||||
skew_factor_x = None
|
||||
skew_factor_y = None
|
||||
mirror = None
|
||||
|
@ -351,7 +355,7 @@ class Film(AppTool):
|
|||
if self.ui.film_mirror_axis.get_value() != 'none':
|
||||
mirror = self.ui.film_mirror_axis.get_value()
|
||||
|
||||
border = float(self.ui.boundary_entry.get_value())
|
||||
border = self.ui.boundary_entry.get_value()
|
||||
|
||||
if border is None:
|
||||
border = 0
|
||||
|
@ -390,7 +394,7 @@ class Film(AppTool):
|
|||
|
||||
def export_negative(self, obj_name, box_name, filename, boundary,
|
||||
scale_stroke_factor=0.00,
|
||||
scale_factor_x=None, scale_factor_y=None,
|
||||
scale_factor_x=1, scale_factor_y=1,
|
||||
skew_factor_x=None, skew_factor_y=None, skew_reference='center',
|
||||
mirror=None,
|
||||
use_thread=True, ftype='svg'):
|
||||
|
@ -434,17 +438,86 @@ class Film(AppTool):
|
|||
self.app.inform.emit('[WARNING_NOTCL] %s: %s' % (_("No object Box. Using instead"), obj))
|
||||
box = obj
|
||||
|
||||
def make_negative_film():
|
||||
scale_factor_x = scale_factor_x
|
||||
scale_factor_y = scale_factor_y
|
||||
|
||||
def make_negative_film(scale_factor_x, scale_factor_y):
|
||||
log.debug("FilmTool.export_negative().make_negative_film()")
|
||||
|
||||
scale_reference = 'center'
|
||||
|
||||
default_dpi = 96
|
||||
new_png_dpi = self.ui.png_dpi_spinner.get_value()
|
||||
dpi_rate = new_png_dpi / default_dpi
|
||||
# Determine bounding area for svg export
|
||||
bounds = box.bounds()
|
||||
tr_scale_reference = (bounds[0], bounds[1])
|
||||
|
||||
if dpi_rate != 1 and ftype == 'png':
|
||||
scale_factor_x += dpi_rate
|
||||
scale_factor_y += dpi_rate
|
||||
scale_reference = (bounds[0], bounds[1])
|
||||
|
||||
if box.kind.lower() == 'geometry':
|
||||
flat_geo = []
|
||||
if box.multigeo:
|
||||
for tool in box.tools:
|
||||
flat_geo += box.flatten(box.tools[tool]['solid_geometry'])
|
||||
box_geo = unary_union(flat_geo)
|
||||
else:
|
||||
box_geo = unary_union(box.flatten())
|
||||
else:
|
||||
box_geo = unary_union(box.flatten())
|
||||
|
||||
skew_ref = 'center'
|
||||
if skew_reference != 'center':
|
||||
xmin, ymin, xmax, ymax = box_geo.bounds
|
||||
if skew_reference == 'topleft':
|
||||
skew_ref = (xmin, ymax)
|
||||
elif skew_reference == 'bottomleft':
|
||||
skew_ref = (xmin, ymin)
|
||||
elif skew_reference == 'topright':
|
||||
skew_ref = (xmax, ymax)
|
||||
elif skew_reference == 'bottomright':
|
||||
skew_ref = (xmax, ymin)
|
||||
|
||||
transformed_box_geo = box_geo
|
||||
|
||||
if scale_factor_x and not scale_factor_y:
|
||||
transformed_box_geo = affinity.scale(transformed_box_geo, scale_factor_x, 1.0,
|
||||
origin=tr_scale_reference)
|
||||
elif not scale_factor_x and scale_factor_y:
|
||||
transformed_box_geo = affinity.scale(transformed_box_geo, 1.0, scale_factor_y,
|
||||
origin=tr_scale_reference)
|
||||
elif scale_factor_x and scale_factor_y:
|
||||
transformed_box_geo = affinity.scale(transformed_box_geo, scale_factor_x, scale_factor_y,
|
||||
origin=tr_scale_reference)
|
||||
|
||||
if skew_factor_x and not skew_factor_y:
|
||||
transformed_box_geo = affinity.skew(transformed_box_geo, skew_factor_x, 0.0, origin=skew_ref)
|
||||
elif not skew_factor_x and skew_factor_y:
|
||||
transformed_box_geo = affinity.skew(transformed_box_geo, 0.0, skew_factor_y, origin=skew_ref)
|
||||
elif skew_factor_x and skew_factor_y:
|
||||
transformed_box_geo = affinity.skew(transformed_box_geo, skew_factor_x, skew_factor_y, origin=skew_ref)
|
||||
|
||||
if mirror:
|
||||
if mirror == 'x':
|
||||
transformed_box_geo = affinity.scale(transformed_box_geo, 1.0, -1.0)
|
||||
if mirror == 'y':
|
||||
transformed_box_geo = affinity.scale(transformed_box_geo, -1.0, 1.0)
|
||||
if mirror == 'both':
|
||||
transformed_box_geo = affinity.scale(transformed_box_geo, -1.0, -1.0)
|
||||
|
||||
bounds = transformed_box_geo.bounds
|
||||
size = bounds[2] - bounds[0], bounds[3] - bounds[1]
|
||||
|
||||
exported_svg = obj.export_svg(scale_stroke_factor=scale_stroke_factor,
|
||||
scale_factor_x=scale_factor_x, scale_factor_y=scale_factor_y,
|
||||
skew_factor_x=skew_factor_x, skew_factor_y=skew_factor_y,
|
||||
mirror=mirror
|
||||
mirror=mirror,
|
||||
scale_reference=scale_reference, skew_reference=skew_reference
|
||||
)
|
||||
|
||||
# Determine bounding area for svg export
|
||||
bounds = box.bounds()
|
||||
size = box.size()
|
||||
|
||||
uom = obj.units.lower()
|
||||
|
||||
# Convert everything to strings for use in the xml doc
|
||||
|
@ -514,6 +587,11 @@ class Film(AppTool):
|
|||
doc_final = StringIO(doc_final)
|
||||
drawing = svg2rlg(doc_final)
|
||||
renderPM.drawToFile(drawing, filename, 'PNG')
|
||||
|
||||
# if new_png_dpi == default_dpi:
|
||||
# renderPM.drawToFile(drawing, filename, 'PNG')
|
||||
# else:
|
||||
# renderPM.drawToFile(drawing, filename, 'PNG', dpi=new_png_dpi)
|
||||
except Exception as e:
|
||||
log.debug("FilmTool.export_negative() --> PNG output --> %s" % str(e))
|
||||
return 'fail'
|
||||
|
@ -528,8 +606,7 @@ class Film(AppTool):
|
|||
drawing = svg2rlg(doc_final)
|
||||
|
||||
p_size = self.ui.pagesize_combo.get_value()
|
||||
if p_size == 'Bounds':
|
||||
renderPDF.drawToFile(drawing, filename)
|
||||
if p_size == 'Bounds': renderPDF.drawToFile(drawing, filename)
|
||||
else:
|
||||
if self.ui.orientation_radio.get_value() == 'p':
|
||||
page_size = portrait(self.ui.pagesize[p_size])
|
||||
|
@ -550,23 +627,21 @@ class Film(AppTool):
|
|||
self.app.inform.emit('[success] %s: %s' % (_("Film file exported to"), filename))
|
||||
|
||||
if use_thread is True:
|
||||
proc = self.app.proc_container.new(_("Generating Film ... Please wait."))
|
||||
def job_thread_film():
|
||||
with self.app.proc_container.new(_("Working...")):
|
||||
try:
|
||||
make_negative_film(scale_factor_x=scale_factor_x, scale_factor_y=scale_factor_y)
|
||||
except Exception as e:
|
||||
log.debug("export_negative() process -> %s" % str(e))
|
||||
return
|
||||
|
||||
def job_thread_film(app_obj):
|
||||
try:
|
||||
make_negative_film()
|
||||
except Exception:
|
||||
proc.done()
|
||||
return
|
||||
proc.done()
|
||||
|
||||
self.app.worker_task.emit({'fcn': job_thread_film, 'params': [self]})
|
||||
self.app.worker_task.emit({'fcn': job_thread_film, 'params': []})
|
||||
else:
|
||||
make_negative_film()
|
||||
make_negative_film(scale_factor_x=scale_factor_x, scale_factor_y=scale_factor_y)
|
||||
|
||||
def export_positive(self, obj_name, box_name, filename,
|
||||
scale_stroke_factor=0.00,
|
||||
scale_factor_x=None, scale_factor_y=None,
|
||||
scale_factor_x=1, scale_factor_y=1,
|
||||
skew_factor_x=None, skew_factor_y=None, skew_reference='center',
|
||||
mirror=None, orientation_val='p', pagesize_val='A4', color_val='black', opacity_val=1.0,
|
||||
use_thread=True, ftype='svg'):
|
||||
|
@ -615,18 +690,89 @@ class Film(AppTool):
|
|||
self.inform.emit('[WARNING_NOTCL] %s: %s' % (_("No object Box. Using instead"), obj))
|
||||
box = obj
|
||||
|
||||
scale_factor_x = scale_factor_x
|
||||
scale_factor_y = scale_factor_y
|
||||
|
||||
p_size = pagesize_val
|
||||
orientation = orientation_val
|
||||
color = color_val
|
||||
transparency_level = opacity_val
|
||||
|
||||
def make_positive_film(p_size, orientation, color, transparency_level):
|
||||
def make_positive_film(p_size, orientation, color, transparency_level, scale_factor_x, scale_factor_y):
|
||||
log.debug("FilmTool.export_positive().make_positive_film()")
|
||||
|
||||
scale_reference = 'center'
|
||||
|
||||
default_dpi = 96
|
||||
new_png_dpi = self.ui.png_dpi_spinner.get_value()
|
||||
dpi_rate = new_png_dpi / default_dpi
|
||||
# Determine bounding area for svg export
|
||||
bounds = box.bounds()
|
||||
tr_scale_reference = (bounds[0], bounds[1])
|
||||
|
||||
if dpi_rate != 1 and ftype == 'png':
|
||||
scale_factor_x += dpi_rate
|
||||
scale_factor_y += dpi_rate
|
||||
scale_reference = (bounds[0], bounds[1])
|
||||
|
||||
if box.kind.lower() == 'geometry':
|
||||
flat_geo = []
|
||||
if box.multigeo:
|
||||
for tool in box.tools:
|
||||
flat_geo += box.flatten(box.tools[tool]['solid_geometry'])
|
||||
box_geo = unary_union(flat_geo)
|
||||
else:
|
||||
box_geo = unary_union(box.flatten())
|
||||
else:
|
||||
box_geo = unary_union(box.flatten())
|
||||
|
||||
skew_ref = 'center'
|
||||
if skew_reference != 'center':
|
||||
xmin, ymin, xmax, ymax = box_geo.bounds
|
||||
if skew_reference == 'topleft':
|
||||
skew_ref = (xmin, ymax)
|
||||
elif skew_reference == 'bottomleft':
|
||||
skew_ref = (xmin, ymin)
|
||||
elif skew_reference == 'topright':
|
||||
skew_ref = (xmax, ymax)
|
||||
elif skew_reference == 'bottomright':
|
||||
skew_ref = (xmax, ymin)
|
||||
|
||||
transformed_box_geo = box_geo
|
||||
|
||||
if scale_factor_x and not scale_factor_y:
|
||||
transformed_box_geo = affinity.scale(transformed_box_geo, scale_factor_x, 1.0,
|
||||
origin=tr_scale_reference)
|
||||
elif not scale_factor_x and scale_factor_y:
|
||||
transformed_box_geo = affinity.scale(transformed_box_geo, 1.0, scale_factor_y,
|
||||
origin=tr_scale_reference)
|
||||
elif scale_factor_x and scale_factor_y:
|
||||
transformed_box_geo = affinity.scale(transformed_box_geo, scale_factor_x, scale_factor_y,
|
||||
origin=tr_scale_reference)
|
||||
|
||||
if skew_factor_x and not skew_factor_y:
|
||||
transformed_box_geo = affinity.skew(transformed_box_geo, skew_factor_x, 0.0, origin=skew_ref)
|
||||
elif not skew_factor_x and skew_factor_y:
|
||||
transformed_box_geo = affinity.skew(transformed_box_geo, 0.0, skew_factor_y, origin=skew_ref)
|
||||
elif skew_factor_x and skew_factor_y:
|
||||
transformed_box_geo = affinity.skew(transformed_box_geo, skew_factor_x, skew_factor_y, origin=skew_ref)
|
||||
|
||||
if mirror:
|
||||
if mirror == 'x':
|
||||
transformed_box_geo = affinity.scale(transformed_box_geo, 1.0, -1.0)
|
||||
if mirror == 'y':
|
||||
transformed_box_geo = affinity.scale(transformed_box_geo, -1.0, 1.0)
|
||||
if mirror == 'both':
|
||||
transformed_box_geo = affinity.scale(transformed_box_geo, -1.0, -1.0)
|
||||
|
||||
bounds = transformed_box_geo.bounds
|
||||
size = bounds[2] - bounds[0], bounds[3] - bounds[1]
|
||||
|
||||
exported_svg = obj.export_svg(scale_stroke_factor=scale_stroke_factor,
|
||||
scale_factor_x=scale_factor_x, scale_factor_y=scale_factor_y,
|
||||
skew_factor_x=skew_factor_x, skew_factor_y=skew_factor_y,
|
||||
mirror=mirror
|
||||
mirror=mirror,
|
||||
scale_reference=scale_reference, skew_reference=skew_reference
|
||||
)
|
||||
|
||||
# Change the attributes of the exported SVG
|
||||
|
@ -641,10 +787,6 @@ class Film(AppTool):
|
|||
|
||||
exported_svg = ET.tostring(root)
|
||||
|
||||
# Determine bounding area for svg export
|
||||
bounds = box.bounds()
|
||||
size = box.size()
|
||||
|
||||
# This contain the measure units
|
||||
uom = obj.units.lower()
|
||||
|
||||
|
@ -693,6 +835,11 @@ class Film(AppTool):
|
|||
doc_final = StringIO(doc_final)
|
||||
drawing = svg2rlg(doc_final)
|
||||
renderPM.drawToFile(drawing, filename, 'PNG')
|
||||
|
||||
# if new_png_dpi == default_dpi:
|
||||
# renderPM.drawToFile(drawing, filename, 'PNG')
|
||||
# else:
|
||||
# renderPM.drawToFile(drawing, filename, 'PNG', dpi=new_png_dpi)
|
||||
except Exception as e:
|
||||
log.debug("FilmTool.export_positive() --> PNG output --> %s" % str(e))
|
||||
return 'fail'
|
||||
|
@ -710,9 +857,9 @@ class Film(AppTool):
|
|||
renderPDF.drawToFile(drawing, filename)
|
||||
else:
|
||||
if orientation == 'p':
|
||||
page_size = portrait(self.pagesize[p_size])
|
||||
page_size = portrait(self.ui.pagesize[p_size])
|
||||
else:
|
||||
page_size = landscape(self.pagesize[p_size])
|
||||
page_size = landscape(self.ui.pagesize[p_size])
|
||||
|
||||
my_canvas = canvas.Canvas(filename, pagesize=page_size)
|
||||
my_canvas.translate(bounds[0] * unit, bounds[1] * unit)
|
||||
|
@ -728,21 +875,21 @@ class Film(AppTool):
|
|||
self.app.inform.emit('[success] %s: %s' % (_("Film file exported to"), filename))
|
||||
|
||||
if use_thread is True:
|
||||
proc = self.app.proc_container.new(_("Generating Film ... Please wait."))
|
||||
|
||||
def job_thread_film():
|
||||
try:
|
||||
make_positive_film(p_size=p_size, orientation=orientation, color=color,
|
||||
transparency_level=transparency_level)
|
||||
except Exception:
|
||||
proc.done()
|
||||
return
|
||||
proc.done()
|
||||
with self.app.proc_container.new(_("Working...")):
|
||||
try:
|
||||
make_positive_film(p_size=p_size, orientation=orientation, color=color,
|
||||
transparency_level=transparency_level,
|
||||
scale_factor_x=scale_factor_x, scale_factor_y=scale_factor_y)
|
||||
except Exception as e:
|
||||
log.debug("export_positive() process -> %s" % str(e))
|
||||
return
|
||||
|
||||
self.app.worker_task.emit({'fcn': job_thread_film, 'params': []})
|
||||
else:
|
||||
make_positive_film(p_size=p_size, orientation=orientation, color=color,
|
||||
transparency_level=transparency_level)
|
||||
transparency_level=transparency_level,
|
||||
scale_factor_x=scale_factor_x, scale_factor_y=scale_factor_y)
|
||||
|
||||
def reset_fields(self):
|
||||
self.ui.tf_object_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
|
||||
|
@ -1199,6 +1346,20 @@ class FilmUI:
|
|||
|
||||
self.on_film_type(val='hide')
|
||||
|
||||
# PNG DPI
|
||||
self.png_dpi_label = FCLabel('%s:' % "PNG DPI")
|
||||
self.png_dpi_label.setToolTip(
|
||||
_("Default value is 96 DPI. Change this value to scale the PNG file.")
|
||||
)
|
||||
self.png_dpi_spinner = FCSpinner(callback=self.confirmation_message_int)
|
||||
self.png_dpi_spinner.set_range(0, 100000)
|
||||
|
||||
grid1.addWidget(self.png_dpi_label, 4, 0)
|
||||
grid1.addWidget(self.png_dpi_spinner, 4, 1)
|
||||
|
||||
self.png_dpi_label.hide()
|
||||
self.png_dpi_spinner.hide()
|
||||
|
||||
# Buttons
|
||||
self.film_object_button = FCButton(_("Save Film"))
|
||||
self.film_object_button.setIcon(QtGui.QIcon(self.app.resource_location + '/save_as.png'))
|
||||
|
@ -1214,7 +1375,7 @@ class FilmUI:
|
|||
font-weight: bold;
|
||||
}
|
||||
""")
|
||||
grid1.addWidget(self.film_object_button, 4, 0, 1, 2)
|
||||
grid1.addWidget(self.film_object_button, 6, 0, 1, 2)
|
||||
|
||||
self.layout.addStretch()
|
||||
|
||||
|
@ -1254,11 +1415,22 @@ class FilmUI:
|
|||
self.orientation_radio.show()
|
||||
self.pagesize_label.show()
|
||||
self.pagesize_combo.show()
|
||||
self.png_dpi_label.hide()
|
||||
self.png_dpi_spinner.hide()
|
||||
elif val == 'png':
|
||||
self.png_dpi_label.show()
|
||||
self.png_dpi_spinner.show()
|
||||
self.orientation_label.hide()
|
||||
self.orientation_radio.hide()
|
||||
self.pagesize_label.hide()
|
||||
self.pagesize_combo.hide()
|
||||
else:
|
||||
self.orientation_label.hide()
|
||||
self.orientation_radio.hide()
|
||||
self.pagesize_label.hide()
|
||||
self.pagesize_combo.hide()
|
||||
self.png_dpi_label.hide()
|
||||
self.png_dpi_spinner.hide()
|
||||
|
||||
def on_punch_source(self, val):
|
||||
if val == 'pad' and self.punch_cb.get_value():
|
||||
|
|
|
@ -19,7 +19,7 @@ import numpy as np
|
|||
import simplejson as json
|
||||
import sys
|
||||
|
||||
from shapely.ops import cascaded_union, nearest_points
|
||||
from shapely.ops import unary_union, nearest_points
|
||||
from shapely.geometry import MultiPolygon, Polygon, MultiLineString, LineString, LinearRing, Point
|
||||
|
||||
from matplotlib.backend_bases import KeyEvent as mpl_key_event
|
||||
|
@ -119,6 +119,8 @@ class ToolIsolation(AppTool, Gerber):
|
|||
self.grb_circle_steps = int(self.app.defaults["gerber_circle_steps"])
|
||||
|
||||
self.tooldia = None
|
||||
# store here the tool diameter that is guaranteed to isolate the object
|
||||
self.safe_tooldia = None
|
||||
|
||||
# multiprocessing
|
||||
self.pool = self.app.pool
|
||||
|
@ -225,6 +227,9 @@ class ToolIsolation(AppTool, Gerber):
|
|||
def set_tool_ui(self):
|
||||
self.units = self.app.defaults['units'].upper()
|
||||
|
||||
# reset the value to prepare for another isolation
|
||||
self.safe_tooldia = None
|
||||
|
||||
# try to select in the Gerber combobox the active object
|
||||
try:
|
||||
selected_obj = self.app.collection.get_active()
|
||||
|
@ -313,6 +318,7 @@ class ToolIsolation(AppTool, Gerber):
|
|||
self.ui.iso_overlap_entry.set_value(self.app.defaults["tools_iso_overlap"])
|
||||
self.ui.milling_type_radio.set_value(self.app.defaults["tools_iso_milling_type"])
|
||||
self.ui.combine_passes_cb.set_value(self.app.defaults["tools_iso_combine_passes"])
|
||||
self.ui.valid_cb.set_value(self.app.defaults["tools_iso_check_valid"])
|
||||
self.ui.area_shape_radio.set_value(self.app.defaults["tools_iso_area_shape"])
|
||||
self.ui.poly_int_cb.set_value(self.app.defaults["tools_iso_poly_ints"])
|
||||
self.ui.forced_rest_iso_cb.set_value(self.app.defaults["tools_iso_force"])
|
||||
|
@ -888,6 +894,9 @@ class ToolIsolation(AppTool, Gerber):
|
|||
})
|
||||
|
||||
def on_find_optimal_tooldia(self):
|
||||
self.find_safe_tooldia_worker(is_displayed=True)
|
||||
|
||||
def find_safe_tooldia_worker(self, is_displayed):
|
||||
self.units = self.app.defaults['units'].upper()
|
||||
|
||||
obj_name = self.ui.object_combo.currentText()
|
||||
|
@ -903,85 +912,111 @@ class ToolIsolation(AppTool, Gerber):
|
|||
self.app.inform.emit('[ERROR_NOTCL] %s: %s' % (_("Object not found"), str(obj_name)))
|
||||
return
|
||||
|
||||
proc = self.app.proc_container.new(_("Working..."))
|
||||
def job_thread(app_obj, is_display):
|
||||
with self.app.proc_container.new(_("Working...")) as proc:
|
||||
try:
|
||||
old_disp_number = 0
|
||||
pol_nr = 0
|
||||
app_obj.proc_container.update_view_text(' %d%%' % 0)
|
||||
total_geo = []
|
||||
|
||||
def job_thread(app_obj):
|
||||
try:
|
||||
old_disp_number = 0
|
||||
pol_nr = 0
|
||||
app_obj.proc_container.update_view_text(' %d%%' % 0)
|
||||
total_geo = []
|
||||
for ap in list(fcobj.apertures.keys()):
|
||||
if 'geometry' in fcobj.apertures[ap]:
|
||||
for geo_el in fcobj.apertures[ap]['geometry']:
|
||||
if self.app.abort_flag:
|
||||
# graceful abort requested by the user
|
||||
raise grace
|
||||
|
||||
for ap in list(fcobj.apertures.keys()):
|
||||
if 'geometry' in fcobj.apertures[ap]:
|
||||
for geo_el in fcobj.apertures[ap]['geometry']:
|
||||
if 'solid' in geo_el and geo_el['solid'] is not None and geo_el['solid'].is_valid:
|
||||
total_geo.append(geo_el['solid'])
|
||||
|
||||
total_geo = MultiPolygon(total_geo)
|
||||
total_geo = total_geo.buffer(0)
|
||||
|
||||
try:
|
||||
__ = iter(total_geo)
|
||||
geo_len = len(total_geo)
|
||||
geo_len = (geo_len * (geo_len - 1)) / 2
|
||||
except TypeError:
|
||||
msg = _("The Gerber object has one Polygon as geometry.\n"
|
||||
"There are no distances between geometry elements to be found.")
|
||||
app_obj.inform.emit('[ERROR_NOTCL] %s' % msg)
|
||||
return 'fail'
|
||||
|
||||
min_dict = {}
|
||||
idx = 1
|
||||
for geo in total_geo:
|
||||
for s_geo in total_geo[idx:]:
|
||||
if self.app.abort_flag:
|
||||
# graceful abort requested by the user
|
||||
raise grace
|
||||
|
||||
if 'solid' in geo_el and geo_el['solid'] is not None and geo_el['solid'].is_valid:
|
||||
total_geo.append(geo_el['solid'])
|
||||
# minimize the number of distances by not taking into considerations those
|
||||
# that are too small
|
||||
dist = geo.distance(s_geo)
|
||||
dist = float('%.*f' % (self.decimals, dist))
|
||||
loc_1, loc_2 = nearest_points(geo, s_geo)
|
||||
|
||||
total_geo = MultiPolygon(total_geo)
|
||||
total_geo = total_geo.buffer(0)
|
||||
proc_loc = (
|
||||
(float('%.*f' % (self.decimals, loc_1.x)), float('%.*f' % (self.decimals, loc_1.y))),
|
||||
(float('%.*f' % (self.decimals, loc_2.x)), float('%.*f' % (self.decimals, loc_2.y)))
|
||||
)
|
||||
|
||||
try:
|
||||
__ = iter(total_geo)
|
||||
geo_len = len(total_geo)
|
||||
geo_len = (geo_len * (geo_len - 1)) / 2
|
||||
except TypeError:
|
||||
app_obj.inform.emit('[ERROR_NOTCL] %s' %
|
||||
_("The Gerber object has one Polygon as geometry.\n"
|
||||
"There are no distances between geometry elements to be found."))
|
||||
return 'fail'
|
||||
if dist in min_dict:
|
||||
min_dict[dist].append(proc_loc)
|
||||
else:
|
||||
min_dict[dist] = [proc_loc]
|
||||
|
||||
min_dict = {}
|
||||
idx = 1
|
||||
for geo in total_geo:
|
||||
for s_geo in total_geo[idx:]:
|
||||
if self.app.abort_flag:
|
||||
# graceful abort requested by the user
|
||||
raise grace
|
||||
pol_nr += 1
|
||||
disp_number = int(np.interp(pol_nr, [0, geo_len], [0, 100]))
|
||||
|
||||
# minimize the number of distances by not taking into considerations those that are too small
|
||||
dist = geo.distance(s_geo)
|
||||
dist = float('%.*f' % (self.decimals, dist))
|
||||
loc_1, loc_2 = nearest_points(geo, s_geo)
|
||||
if old_disp_number < disp_number <= 100:
|
||||
app_obj.proc_container.update_view_text(' %d%%' % disp_number)
|
||||
old_disp_number = disp_number
|
||||
idx += 1
|
||||
|
||||
proc_loc = (
|
||||
(float('%.*f' % (self.decimals, loc_1.x)), float('%.*f' % (self.decimals, loc_1.y))),
|
||||
(float('%.*f' % (self.decimals, loc_2.x)), float('%.*f' % (self.decimals, loc_2.y)))
|
||||
)
|
||||
min_list = list(min_dict.keys())
|
||||
min_dist = min(min_list)
|
||||
|
||||
if dist in min_dict:
|
||||
min_dict[dist].append(proc_loc)
|
||||
else:
|
||||
min_dict[dist] = [proc_loc]
|
||||
min_dist_truncated = self.app.dec_format(float(min_dist), self.decimals)
|
||||
self.safe_tooldia = min_dist_truncated
|
||||
|
||||
pol_nr += 1
|
||||
disp_number = int(np.interp(pol_nr, [0, geo_len], [0, 100]))
|
||||
if is_display:
|
||||
self.optimal_found_sig.emit(min_dist_truncated)
|
||||
|
||||
if old_disp_number < disp_number <= 100:
|
||||
app_obj.proc_container.update_view_text(' %d%%' % disp_number)
|
||||
old_disp_number = disp_number
|
||||
idx += 1
|
||||
app_obj.inform.emit('[success] %s: %s %s' %
|
||||
(_("Optimal tool diameter found"), str(min_dist_truncated),
|
||||
self.units.lower()))
|
||||
else:
|
||||
if self.safe_tooldia:
|
||||
# find the selected tool ID's
|
||||
sorted_tools = []
|
||||
table_items = self.ui.tools_table.selectedItems()
|
||||
sel_rows = {t.row() for t in table_items}
|
||||
for row in sel_rows:
|
||||
tid = int(self.ui.tools_table.item(row, 3).text())
|
||||
sorted_tools.append(tid)
|
||||
if not sorted_tools:
|
||||
msg = _("There are no tools selected in the Tool Table.")
|
||||
self.app.inform.emit('[ERROR_NOTCL] %s' % msg)
|
||||
return 'fail'
|
||||
|
||||
min_list = list(min_dict.keys())
|
||||
min_dist = min(min_list)
|
||||
# check if the tools diameters are less then the safe tool diameter
|
||||
for tool in sorted_tools:
|
||||
tool_dia = float(self.iso_tools[tool]['tooldia'])
|
||||
if tool_dia > self.safe_tooldia:
|
||||
msg = _("Incomplete isolation. "
|
||||
"At least one tool could not do a complete isolation.")
|
||||
self.app.inform.emit('[WARNING] %s' % msg)
|
||||
break
|
||||
|
||||
min_dist_truncated = self.app.dec_format(float(min_dist), self.decimals)
|
||||
# reset the value to prepare for another isolation
|
||||
self.safe_tooldia = None
|
||||
except Exception as ee:
|
||||
log.debug(str(ee))
|
||||
return
|
||||
|
||||
self.optimal_found_sig.emit(min_dist_truncated)
|
||||
|
||||
app_obj.inform.emit('[success] %s: %s %s' %
|
||||
(_("Optimal tool diameter found"), str(min_dist_truncated), self.units.lower()))
|
||||
except Exception as ee:
|
||||
proc.done()
|
||||
log.debug(str(ee))
|
||||
return
|
||||
proc.done()
|
||||
|
||||
self.app.worker_task.emit({'fcn': job_thread, 'params': [self.app]})
|
||||
self.app.worker_task.emit({'fcn': job_thread, 'params': [self.app, is_displayed]})
|
||||
|
||||
def on_tool_add(self, custom_dia=None):
|
||||
self.blockSignals(True)
|
||||
|
@ -1333,14 +1368,17 @@ class ToolIsolation(AppTool, Gerber):
|
|||
# Get source object.
|
||||
try:
|
||||
self.grb_obj = self.app.collection.get_by_name(self.obj_name)
|
||||
except Exception as e:
|
||||
except Exception:
|
||||
self.app.inform.emit('[ERROR_NOTCL] %s: %s' % (_("Could not retrieve object"), str(self.obj_name)))
|
||||
return "Could not retrieve object: %s with error: %s" % (self.obj_name, str(e))
|
||||
return
|
||||
|
||||
if self.grb_obj is None:
|
||||
self.app.inform.emit('[ERROR_NOTCL] %s: %s' % (_("Object not found"), str(self.obj_name)))
|
||||
return
|
||||
|
||||
if self.ui.valid_cb.get_value() is True:
|
||||
self.find_safe_tooldia_worker(is_displayed=False)
|
||||
|
||||
def worker_task(iso_obj):
|
||||
with self.app.proc_container.new(_("Isolating...")):
|
||||
self.isolate_handler(iso_obj)
|
||||
|
@ -1436,8 +1474,8 @@ class ToolIsolation(AppTool, Gerber):
|
|||
|
||||
elif selection == _("Reference Object"):
|
||||
ref_obj = self.app.collection.get_by_name(self.ui.reference_combo.get_value())
|
||||
ref_geo = cascaded_union(ref_obj.solid_geometry)
|
||||
use_geo = cascaded_union(isolated_obj.solid_geometry).difference(ref_geo)
|
||||
ref_geo = unary_union(ref_obj.solid_geometry)
|
||||
use_geo = unary_union(isolated_obj.solid_geometry).difference(ref_geo)
|
||||
self.isolate(isolated_obj=isolated_obj, geometry=use_geo)
|
||||
|
||||
def isolate(self, isolated_obj, geometry=None, limited_area=None, negative_dia=None, plot=True):
|
||||
|
@ -1467,7 +1505,7 @@ class ToolIsolation(AppTool, Gerber):
|
|||
tid = int(self.ui.tools_table.item(row, 3).text())
|
||||
sorted_tools.append(tid)
|
||||
if not sorted_tools:
|
||||
self.app.inform.emit('[ERROR_NOTCL] %s' % _("No selected tools in Tool Table."))
|
||||
self.app.inform.emit('[ERROR_NOTCL] %s' % _("There are no tools selected in the Tool Table."))
|
||||
return 'fail'
|
||||
|
||||
# update the Common Parameters values in the self.iso_tools
|
||||
|
@ -1669,7 +1707,7 @@ class ToolIsolation(AppTool, Gerber):
|
|||
sorted_tools.append(float('%.*f' % (self.decimals, tdia)))
|
||||
|
||||
if not sorted_tools:
|
||||
self.app.inform.emit('[ERROR_NOTCL] %s' % _("No selected tools in Tool Table."))
|
||||
self.app.inform.emit('[ERROR_NOTCL] %s' % _("There are no tools selected in the Tool Table."))
|
||||
return 'fail'
|
||||
|
||||
order = self.ui.order_radio.get_value()
|
||||
|
@ -1856,7 +1894,7 @@ class ToolIsolation(AppTool, Gerber):
|
|||
tid = int(self.ui.tools_table.item(row, 3).text())
|
||||
sorted_tools.append(tid)
|
||||
if not sorted_tools:
|
||||
self.app.inform.emit('[ERROR_NOTCL] %s' % _("No selected tools in Tool Table."))
|
||||
self.app.inform.emit('[ERROR_NOTCL] %s' % _("There are no tools selected in the Tool Table."))
|
||||
return 'fail'
|
||||
|
||||
for tool in sorted_tools:
|
||||
|
@ -2010,11 +2048,11 @@ class ToolIsolation(AppTool, Gerber):
|
|||
target_geo = geo
|
||||
|
||||
if subtraction_geo:
|
||||
sub_union = cascaded_union(subtraction_geo)
|
||||
sub_union = unary_union(subtraction_geo)
|
||||
else:
|
||||
name = self.ui.exc_obj_combo.currentText()
|
||||
subtractor_obj = self.app.collection.get_by_name(name)
|
||||
sub_union = cascaded_union(subtractor_obj.solid_geometry)
|
||||
sub_union = unary_union(subtractor_obj.solid_geometry)
|
||||
|
||||
try:
|
||||
for geo_elem in target_geo:
|
||||
|
@ -2068,7 +2106,7 @@ class ToolIsolation(AppTool, Gerber):
|
|||
new_geometry = []
|
||||
target_geo = geo
|
||||
|
||||
intersect_union = cascaded_union(intersection_geo)
|
||||
intersect_union = unary_union(intersection_geo)
|
||||
|
||||
try:
|
||||
for geo_elem in target_geo:
|
||||
|
@ -2245,7 +2283,7 @@ class ToolIsolation(AppTool, Gerber):
|
|||
except TypeError:
|
||||
if self.solid_geometry not in self.poly_dict.values():
|
||||
if sel_type is True:
|
||||
if self.solid_geometry.within(poly_selection):
|
||||
if poly_selection.contains(self.solid_geometry):
|
||||
shape_id = self.app.tool_shapes.add(tolerance=self.drawing_tolerance, layer=0,
|
||||
shape=self.solid_geometry,
|
||||
color=self.app.defaults['global_sel_draw_color'] + 'AF',
|
||||
|
@ -2389,7 +2427,7 @@ class ToolIsolation(AppTool, Gerber):
|
|||
if len(self.sel_rect) == 0:
|
||||
return
|
||||
|
||||
self.sel_rect = cascaded_union(self.sel_rect)
|
||||
self.sel_rect = unary_union(self.sel_rect)
|
||||
self.isolate(isolated_obj=self.grb_obj, limited_area=self.sel_rect, plot=True)
|
||||
self.sel_rect = []
|
||||
|
||||
|
@ -3096,8 +3134,9 @@ class IsoUI:
|
|||
self.addtool_from_db_btn.setIcon(QtGui.QIcon(self.app.resource_location + '/search_db32.png'))
|
||||
self.addtool_from_db_btn.setToolTip(
|
||||
_("Add a new tool to the Tool Table\n"
|
||||
"from the Tool Database.\n"
|
||||
"Tool database administration in Menu: Options -> Tools Database")
|
||||
"from the Tools Database.\n"
|
||||
"Tools database administration in in:\n"
|
||||
"Menu: Options -> Tools Database")
|
||||
)
|
||||
bhlay.addWidget(self.addtool_from_db_btn)
|
||||
|
||||
|
@ -3277,13 +3316,23 @@ class IsoUI:
|
|||
|
||||
self.grid3.addWidget(self.combine_passes_cb, 26, 0, 1, 2)
|
||||
|
||||
# Check Tool validity
|
||||
self.valid_cb = FCCheckBox(label=_('Check validity'))
|
||||
self.valid_cb.setToolTip(
|
||||
_("If checked then the tools diameters are verified\n"
|
||||
"if they will provide a complete isolation.")
|
||||
)
|
||||
self.valid_cb.setObjectName("i_check")
|
||||
|
||||
self.grid3.addWidget(self.valid_cb, 28, 0, 1, 2)
|
||||
|
||||
# Exception Areas
|
||||
self.except_cb = FCCheckBox(label=_('Except'))
|
||||
self.except_cb.setToolTip(_("When the isolation geometry is generated,\n"
|
||||
"by checking this, the area of the object below\n"
|
||||
"will be subtracted from the isolation geometry."))
|
||||
self.except_cb.setObjectName("i_except")
|
||||
self.grid3.addWidget(self.except_cb, 27, 0)
|
||||
self.grid3.addWidget(self.except_cb, 30, 0)
|
||||
|
||||
# Type of object to be excepted
|
||||
self.type_excobj_radio = RadioSet([{'label': _("Geometry"), 'value': 'geometry'},
|
||||
|
@ -3295,7 +3344,7 @@ class IsoUI:
|
|||
"of objects that will populate the 'Object' combobox.")
|
||||
)
|
||||
|
||||
self.grid3.addWidget(self.type_excobj_radio, 27, 1)
|
||||
self.grid3.addWidget(self.type_excobj_radio, 30, 1)
|
||||
|
||||
# The object to be excepted
|
||||
self.exc_obj_combo = FCComboBox()
|
||||
|
@ -3307,7 +3356,7 @@ class IsoUI:
|
|||
self.exc_obj_combo.is_last = True
|
||||
self.exc_obj_combo.obj_type = "gerber"
|
||||
|
||||
self.grid3.addWidget(self.exc_obj_combo, 28, 0, 1, 2)
|
||||
self.grid3.addWidget(self.exc_obj_combo, 32, 0, 1, 2)
|
||||
|
||||
self.e_ois = OptionalInputSection(self.except_cb,
|
||||
[
|
||||
|
@ -3330,8 +3379,8 @@ class IsoUI:
|
|||
)
|
||||
self.select_combo.setObjectName("i_selection")
|
||||
|
||||
self.grid3.addWidget(self.select_label, 33, 0)
|
||||
self.grid3.addWidget(self.select_combo, 33, 1)
|
||||
self.grid3.addWidget(self.select_label, 34, 0)
|
||||
self.grid3.addWidget(self.select_combo, 34, 1)
|
||||
|
||||
self.reference_combo_type_label = FCLabel('%s:' % _("Ref. Type"))
|
||||
self.reference_combo_type_label.setToolTip(
|
||||
|
@ -3341,8 +3390,8 @@ class IsoUI:
|
|||
self.reference_combo_type = FCComboBox()
|
||||
self.reference_combo_type.addItems([_("Gerber"), _("Excellon"), _("Geometry")])
|
||||
|
||||
self.grid3.addWidget(self.reference_combo_type_label, 34, 0)
|
||||
self.grid3.addWidget(self.reference_combo_type, 34, 1)
|
||||
self.grid3.addWidget(self.reference_combo_type_label, 36, 0)
|
||||
self.grid3.addWidget(self.reference_combo_type, 36, 1)
|
||||
|
||||
self.reference_combo_label = FCLabel('%s:' % _("Ref. Object"))
|
||||
self.reference_combo_label.setToolTip(
|
||||
|
@ -3353,8 +3402,8 @@ class IsoUI:
|
|||
self.reference_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
|
||||
self.reference_combo.is_last = True
|
||||
|
||||
self.grid3.addWidget(self.reference_combo_label, 35, 0)
|
||||
self.grid3.addWidget(self.reference_combo, 35, 1)
|
||||
self.grid3.addWidget(self.reference_combo_label, 38, 0)
|
||||
self.grid3.addWidget(self.reference_combo, 38, 1)
|
||||
|
||||
self.reference_combo.hide()
|
||||
self.reference_combo_label.hide()
|
||||
|
@ -3368,7 +3417,7 @@ class IsoUI:
|
|||
"(holes in the polygon).")
|
||||
)
|
||||
|
||||
self.grid3.addWidget(self.poly_int_cb, 36, 0)
|
||||
self.grid3.addWidget(self.poly_int_cb, 40, 0)
|
||||
|
||||
self.poly_int_cb.hide()
|
||||
|
||||
|
@ -3381,8 +3430,8 @@ class IsoUI:
|
|||
self.area_shape_radio = RadioSet([{'label': _("Square"), 'value': 'square'},
|
||||
{'label': _("Polygon"), 'value': 'polygon'}])
|
||||
|
||||
self.grid3.addWidget(self.area_shape_label, 38, 0)
|
||||
self.grid3.addWidget(self.area_shape_radio, 38, 1)
|
||||
self.grid3.addWidget(self.area_shape_label, 42, 0)
|
||||
self.grid3.addWidget(self.area_shape_radio, 42, 1)
|
||||
|
||||
self.area_shape_label.hide()
|
||||
self.area_shape_radio.hide()
|
||||
|
@ -3390,7 +3439,7 @@ class IsoUI:
|
|||
separator_line = QtWidgets.QFrame()
|
||||
separator_line.setFrameShape(QtWidgets.QFrame.HLine)
|
||||
separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
|
||||
self.grid3.addWidget(separator_line, 39, 0, 1, 2)
|
||||
self.grid3.addWidget(separator_line, 44, 0, 1, 2)
|
||||
|
||||
self.generate_iso_button = FCButton("%s" % _("Generate Geometry"))
|
||||
self.generate_iso_button.setIcon(QtGui.QIcon(self.app.resource_location + '/geometry32.png'))
|
||||
|
|
|
@ -17,7 +17,7 @@ from copy import deepcopy
|
|||
# import numpy as np
|
||||
# import math
|
||||
|
||||
# from shapely.ops import cascaded_union
|
||||
# from shapely.ops import unary_union
|
||||
from shapely.geometry import Point, LineString
|
||||
|
||||
from matplotlib.backend_bases import KeyEvent as mpl_key_event
|
||||
|
|
|
@ -18,7 +18,7 @@ from copy import deepcopy
|
|||
|
||||
import numpy as np
|
||||
from shapely.geometry import base
|
||||
from shapely.ops import cascaded_union, nearest_points
|
||||
from shapely.ops import unary_union, nearest_points
|
||||
from shapely.geometry import MultiPolygon, Polygon, MultiLineString, LineString, LinearRing
|
||||
|
||||
from matplotlib.backend_bases import KeyEvent as mpl_key_event
|
||||
|
@ -548,6 +548,7 @@ class NonCopperClear(AppTool, Gerber):
|
|||
"area_shape": self.app.defaults["geometry_area_shape"],
|
||||
"area_strategy": self.app.defaults["geometry_area_strategy"],
|
||||
"area_overz": float(self.app.defaults["geometry_area_overz"]),
|
||||
"optimization_type": self.app.defaults["geometry_optimization_type"],
|
||||
|
||||
"tools_nccoperation": self.app.defaults["tools_nccoperation"],
|
||||
"tools_nccmargin": self.app.defaults["tools_nccmargin"],
|
||||
|
@ -1292,7 +1293,7 @@ class NonCopperClear(AppTool, Gerber):
|
|||
else:
|
||||
self.ncc_dia_list.append(self.tooldia)
|
||||
else:
|
||||
self.app.inform.emit('[ERROR_NOTCL] %s' % _("No selected tools in Tool Table."))
|
||||
self.app.inform.emit('[ERROR_NOTCL] %s' % _("There are no tools selected in the Tool Table."))
|
||||
return
|
||||
|
||||
self.o_name = '%s_ncc' % self.obj_name
|
||||
|
@ -1460,7 +1461,7 @@ class NonCopperClear(AppTool, Gerber):
|
|||
if len(self.sel_rect) == 0:
|
||||
return
|
||||
|
||||
self.sel_rect = cascaded_union(self.sel_rect)
|
||||
self.sel_rect = unary_union(self.sel_rect)
|
||||
|
||||
self.clear_copper(ncc_obj=self.ncc_obj, sel_obj=self.bound_obj, ncctooldia=self.ncc_dia_list,
|
||||
isotooldia=self.iso_dia_list, outname=self.o_name)
|
||||
|
@ -1622,16 +1623,16 @@ class NonCopperClear(AppTool, Gerber):
|
|||
env_obj = geo_n.convex_hull
|
||||
elif (isinstance(geo_n, MultiPolygon) and len(geo_n) == 1) or \
|
||||
(isinstance(geo_n, list) and len(geo_n) == 1) and isinstance(geo_n[0], Polygon):
|
||||
env_obj = cascaded_union(geo_n)
|
||||
env_obj = unary_union(geo_n)
|
||||
else:
|
||||
env_obj = cascaded_union(geo_n)
|
||||
env_obj = unary_union(geo_n)
|
||||
env_obj = env_obj.convex_hull
|
||||
except Exception as e:
|
||||
log.debug("NonCopperClear.calculate_bounding_box() 'itself' --> %s" % str(e))
|
||||
self.app.inform.emit('[ERROR_NOTCL] %s' % _("No object available."))
|
||||
return None
|
||||
elif ncc_select == _("Area Selection"):
|
||||
env_obj = cascaded_union(self.sel_rect)
|
||||
env_obj = unary_union(self.sel_rect)
|
||||
try:
|
||||
__ = iter(env_obj)
|
||||
except Exception:
|
||||
|
@ -1649,8 +1650,8 @@ class NonCopperClear(AppTool, Gerber):
|
|||
env_obj = [box_geo]
|
||||
|
||||
elif box_kind == 'gerber':
|
||||
box_geo = cascaded_union(box_obj.solid_geometry).convex_hull
|
||||
ncc_geo = cascaded_union(ncc_obj.solid_geometry).convex_hull
|
||||
box_geo = unary_union(box_obj.solid_geometry).convex_hull
|
||||
ncc_geo = unary_union(ncc_obj.solid_geometry).convex_hull
|
||||
env_obj = ncc_geo.intersection(box_geo)
|
||||
else:
|
||||
self.app.inform.emit('[ERROR_NOTCL] %s' % _("The reference object type is not supported."))
|
||||
|
@ -1692,7 +1693,7 @@ class NonCopperClear(AppTool, Gerber):
|
|||
# graceful abort requested by the user
|
||||
raise grace
|
||||
geo_buff_list.append(poly.buffer(distance=ncc_margin, join_style=base.JOIN_STYLE.mitre))
|
||||
new_bounding_box = cascaded_union(geo_buff_list)
|
||||
new_bounding_box = unary_union(geo_buff_list)
|
||||
elif ncc_select == _("Reference Object"):
|
||||
if box_kind == 'geometry':
|
||||
geo_buff_list = []
|
||||
|
@ -1702,7 +1703,7 @@ class NonCopperClear(AppTool, Gerber):
|
|||
raise grace
|
||||
geo_buff_list.append(poly.buffer(distance=ncc_margin, join_style=base.JOIN_STYLE.mitre))
|
||||
|
||||
new_bounding_box = cascaded_union(geo_buff_list)
|
||||
new_bounding_box = unary_union(geo_buff_list)
|
||||
elif box_kind == 'gerber':
|
||||
new_bounding_box = bbox.buffer(distance=ncc_margin, join_style=base.JOIN_STYLE.mitre)
|
||||
else:
|
||||
|
@ -1873,7 +1874,7 @@ class NonCopperClear(AppTool, Gerber):
|
|||
geo_obj.tools[current_uid] = dict(tools_storage[current_uid])
|
||||
break
|
||||
|
||||
sol_geo = cascaded_union(isolated_geo)
|
||||
sol_geo = unary_union(isolated_geo)
|
||||
if has_offset is True:
|
||||
self.app.inform.emit('[WARNING_NOTCL] %s ...' % _("Buffering"))
|
||||
sol_geo = sol_geo.buffer(distance=ncc_offset)
|
||||
|
@ -1886,7 +1887,7 @@ class NonCopperClear(AppTool, Gerber):
|
|||
return 'fail'
|
||||
|
||||
elif ncc_obj.kind == 'geometry':
|
||||
sol_geo = cascaded_union(ncc_obj.solid_geometry)
|
||||
sol_geo = unary_union(ncc_obj.solid_geometry)
|
||||
if has_offset is True:
|
||||
self.app.inform.emit('[WARNING_NOTCL] %s ...' % _("Buffering"))
|
||||
sol_geo = sol_geo.buffer(distance=ncc_offset)
|
||||
|
@ -2430,7 +2431,7 @@ class NonCopperClear(AppTool, Gerber):
|
|||
log.debug("There are no geometries in the cleared polygon.")
|
||||
|
||||
# Area to clear next
|
||||
buffered_cleared = cascaded_union(cleared_geo).buffer(tool / 2.0)
|
||||
buffered_cleared = unary_union(cleared_geo).buffer(tool / 2.0)
|
||||
area = area.difference(buffered_cleared)
|
||||
|
||||
if not area or area.is_empty:
|
||||
|
@ -2442,7 +2443,7 @@ class NonCopperClear(AppTool, Gerber):
|
|||
# new_area = [p.buffer(buff_distance) for p in area if not p.is_empty]
|
||||
# except TypeError:
|
||||
# new_area = [area.buffer(tool * ncc_overlap)]
|
||||
# area = cascaded_union(area)
|
||||
# area = unary_union(area)
|
||||
|
||||
geo_obj.multigeo = True
|
||||
geo_obj.options["cnctooldia"] = '0.0'
|
||||
|
@ -2615,9 +2616,9 @@ class NonCopperClear(AppTool, Gerber):
|
|||
env_obj = geo_n.convex_hull
|
||||
elif (isinstance(geo_n, MultiPolygon) and len(geo_n) == 1) or \
|
||||
(isinstance(geo_n, list) and len(geo_n) == 1) and isinstance(geo_n[0], Polygon):
|
||||
env_obj = cascaded_union(geo_n)
|
||||
env_obj = unary_union(geo_n)
|
||||
else:
|
||||
env_obj = cascaded_union(geo_n)
|
||||
env_obj = unary_union(geo_n)
|
||||
env_obj = env_obj.convex_hull
|
||||
|
||||
bounding_box = env_obj.buffer(distance=ncc_margin, join_style=base.JOIN_STYLE.mitre)
|
||||
|
@ -2627,7 +2628,7 @@ class NonCopperClear(AppTool, Gerber):
|
|||
return 'fail'
|
||||
|
||||
elif ncc_select == 'area':
|
||||
geo_n = cascaded_union(self.sel_rect)
|
||||
geo_n = unary_union(self.sel_rect)
|
||||
try:
|
||||
__ = iter(geo_n)
|
||||
except Exception as e:
|
||||
|
@ -2641,7 +2642,7 @@ class NonCopperClear(AppTool, Gerber):
|
|||
raise grace
|
||||
geo_buff_list.append(poly.buffer(distance=ncc_margin, join_style=base.JOIN_STYLE.mitre))
|
||||
|
||||
bounding_box = cascaded_union(geo_buff_list)
|
||||
bounding_box = unary_union(geo_buff_list)
|
||||
|
||||
elif ncc_select == _("Reference Object"):
|
||||
geo_n = ncc_sel_obj.solid_geometry
|
||||
|
@ -2659,10 +2660,10 @@ class NonCopperClear(AppTool, Gerber):
|
|||
raise grace
|
||||
geo_buff_list.append(poly.buffer(distance=ncc_margin, join_style=base.JOIN_STYLE.mitre))
|
||||
|
||||
bounding_box = cascaded_union(geo_buff_list)
|
||||
bounding_box = unary_union(geo_buff_list)
|
||||
elif ncc_sel_obj.kind == 'gerber':
|
||||
geo_n = cascaded_union(geo_n).convex_hull
|
||||
bounding_box = cascaded_union(self.ncc_obj.solid_geometry).convex_hull.intersection(geo_n)
|
||||
geo_n = unary_union(geo_n).convex_hull
|
||||
bounding_box = unary_union(self.ncc_obj.solid_geometry).convex_hull.intersection(geo_n)
|
||||
bounding_box = bounding_box.buffer(distance=ncc_margin, join_style=base.JOIN_STYLE.mitre)
|
||||
else:
|
||||
self.app.inform.emit('[ERROR_NOTCL] %s' % _("The reference object type is not supported."))
|
||||
|
@ -2837,7 +2838,7 @@ class NonCopperClear(AppTool, Gerber):
|
|||
break
|
||||
geo_obj.tools[current_uid] = dict(tools_storage[current_uid])
|
||||
|
||||
sol_geo = cascaded_union(isolated_geo)
|
||||
sol_geo = unary_union(isolated_geo)
|
||||
if has_offset is True:
|
||||
app_obj.inform.emit('[WARNING_NOTCL] %s ...' % _("Buffering"))
|
||||
sol_geo = sol_geo.buffer(distance=ncc_offset)
|
||||
|
@ -2852,7 +2853,7 @@ class NonCopperClear(AppTool, Gerber):
|
|||
return 'fail'
|
||||
|
||||
elif ncc_obj.kind == 'geometry':
|
||||
sol_geo = cascaded_union(ncc_obj.solid_geometry)
|
||||
sol_geo = unary_union(ncc_obj.solid_geometry)
|
||||
if has_offset is True:
|
||||
app_obj.inform.emit('[WARNING_NOTCL] %s ...' % _("Buffering"))
|
||||
sol_geo = sol_geo.buffer(distance=ncc_offset)
|
||||
|
@ -3219,7 +3220,7 @@ class NonCopperClear(AppTool, Gerber):
|
|||
break
|
||||
geo_obj.tools[current_uid] = dict(tools_storage[current_uid])
|
||||
|
||||
sol_geo = cascaded_union(isolated_geo)
|
||||
sol_geo = unary_union(isolated_geo)
|
||||
if has_offset is True:
|
||||
app_obj.inform.emit('[WARNING_NOTCL] %s ...' % _("Buffering"))
|
||||
sol_geo = sol_geo.buffer(distance=ncc_offset)
|
||||
|
@ -3234,7 +3235,7 @@ class NonCopperClear(AppTool, Gerber):
|
|||
return 'fail'
|
||||
|
||||
elif ncc_obj.kind == 'geometry':
|
||||
sol_geo = cascaded_union(ncc_obj.solid_geometry)
|
||||
sol_geo = unary_union(ncc_obj.solid_geometry)
|
||||
if has_offset is True:
|
||||
app_obj.inform.emit('[WARNING_NOTCL] %s ...' % _("Buffering"))
|
||||
sol_geo = sol_geo.buffer(distance=ncc_offset)
|
||||
|
@ -3857,26 +3858,31 @@ class NccUI:
|
|||
# ### Tool Diameter ####
|
||||
self.new_tooldia_lbl = FCLabel('%s:' % _('Tool Dia'))
|
||||
self.new_tooldia_lbl.setToolTip(
|
||||
_("Diameter for the new tool to add in the Tool Table.\n"
|
||||
"If the tool is V-shape type then this value is automatically\n"
|
||||
"calculated from the other parameters.")
|
||||
_("Diameter for the new tool")
|
||||
)
|
||||
self.grid3.addWidget(self.new_tooldia_lbl, 2, 0)
|
||||
|
||||
new_tool_lay = QtWidgets.QHBoxLayout()
|
||||
|
||||
self.new_tooldia_entry = FCDoubleSpinner(callback=self.confirmation_message)
|
||||
self.new_tooldia_entry.set_precision(self.decimals)
|
||||
self.new_tooldia_entry.set_range(0.000, 9999.9999)
|
||||
self.new_tooldia_entry.setObjectName(_("Tool Dia"))
|
||||
|
||||
self.grid3.addWidget(self.new_tooldia_lbl, 2, 0)
|
||||
self.grid3.addWidget(self.new_tooldia_entry, 2, 1)
|
||||
new_tool_lay.addWidget(self.new_tooldia_entry)
|
||||
|
||||
# Find Optimal Tooldia
|
||||
self.find_optimal_button = FCButton(_('Find Optimal'))
|
||||
self.find_optimal_button = QtWidgets.QToolButton()
|
||||
self.find_optimal_button.setText(_('Optimal'))
|
||||
self.find_optimal_button.setIcon(QtGui.QIcon(self.app.resource_location + '/open_excellon32.png'))
|
||||
self.find_optimal_button.setToolButtonStyle(QtCore.Qt.ToolButtonTextBesideIcon)
|
||||
self.find_optimal_button.setToolTip(
|
||||
_("Find a tool diameter that is guaranteed\n"
|
||||
"to do a complete isolation.")
|
||||
)
|
||||
self.grid3.addWidget(self.find_optimal_button, 4, 0, 1, 2)
|
||||
new_tool_lay.addWidget(self.find_optimal_button)
|
||||
|
||||
self.grid3.addLayout(new_tool_lay, 2, 1)
|
||||
|
||||
hlay = QtWidgets.QHBoxLayout()
|
||||
|
||||
|
@ -3895,8 +3901,9 @@ class NccUI:
|
|||
self.addtool_from_db_btn.setIcon(QtGui.QIcon(self.app.resource_location + '/search_db32.png'))
|
||||
self.addtool_from_db_btn.setToolTip(
|
||||
_("Add a new tool to the Tool Table\n"
|
||||
"from the Tool Database.\n"
|
||||
"Tool database administration in Menu: Options -> Tools Database")
|
||||
"from the Tools Database.\n"
|
||||
"Tools database administration in in:\n"
|
||||
"Menu: Options -> Tools Database")
|
||||
)
|
||||
hlay.addWidget(self.addtool_from_db_btn)
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@ from appGUI.GUIElements import FCTable, FCDoubleSpinner, FCCheckBox, FCInputDial
|
|||
FCLabel
|
||||
|
||||
from shapely.geometry import base, Polygon, MultiPolygon, LinearRing, Point
|
||||
from shapely.ops import cascaded_union, unary_union, linemerge
|
||||
from shapely.ops import unary_union, linemerge
|
||||
|
||||
from matplotlib.backend_bases import KeyEvent as mpl_key_event
|
||||
|
||||
|
@ -466,6 +466,7 @@ class ToolPaint(AppTool, Gerber):
|
|||
"area_shape": self.app.defaults["geometry_area_shape"],
|
||||
"area_strategy": self.app.defaults["geometry_area_strategy"],
|
||||
"area_overz": float(self.app.defaults["geometry_area_overz"]),
|
||||
"optimization_type": self.app.defaults["geometry_optimization_type"],
|
||||
|
||||
"tooldia": self.app.defaults["tools_painttooldia"],
|
||||
"tools_paintoffset": self.app.defaults["tools_paintoffset"],
|
||||
|
@ -1027,7 +1028,7 @@ class ToolPaint(AppTool, Gerber):
|
|||
continue
|
||||
self.tooldia_list.append(self.tooldia)
|
||||
else:
|
||||
self.app.inform.emit('[ERROR_NOTCL] %s' % _("No selected tools in Tool Table."))
|
||||
self.app.inform.emit('[ERROR_NOTCL] %s' % _("There are no tools selected in the Tool Table."))
|
||||
return
|
||||
|
||||
self.select_method = self.ui.selectmethod_combo.get_value()
|
||||
|
@ -1293,7 +1294,7 @@ class ToolPaint(AppTool, Gerber):
|
|||
if len(self.sel_rect) == 0:
|
||||
return
|
||||
|
||||
self.sel_rect = cascaded_union(self.sel_rect)
|
||||
self.sel_rect = unary_union(self.sel_rect)
|
||||
self.paint_poly_area(obj=self.paint_obj, tooldia=self.tooldia_list, sel_obj=self.sel_rect,
|
||||
outname=self.o_name)
|
||||
|
||||
|
@ -1740,7 +1741,7 @@ class ToolPaint(AppTool, Gerber):
|
|||
continue
|
||||
sorted_tools.append(self.tooldia)
|
||||
if not sorted_tools:
|
||||
self.app.inform.emit('[ERROR_NOTCL] %s' % _("No selected tools in Tool Table."))
|
||||
self.app.inform.emit('[ERROR_NOTCL] %s' % _("There are no tools selected in the Tool Table."))
|
||||
return 'fail'
|
||||
|
||||
# Initializes the new geometry object
|
||||
|
@ -1880,7 +1881,7 @@ class ToolPaint(AppTool, Gerber):
|
|||
geo_obj.tools.clear()
|
||||
geo_obj.tools = dict(tools_storage)
|
||||
|
||||
geo_obj.solid_geometry = cascaded_union(final_solid_geometry)
|
||||
geo_obj.solid_geometry = unary_union(final_solid_geometry)
|
||||
|
||||
try:
|
||||
if isinstance(geo_obj.solid_geometry, list):
|
||||
|
@ -1934,7 +1935,7 @@ class ToolPaint(AppTool, Gerber):
|
|||
except TypeError:
|
||||
poly_buf.append(buffered_pol)
|
||||
|
||||
poly_buf = cascaded_union(poly_buf)
|
||||
poly_buf = unary_union(poly_buf)
|
||||
|
||||
if not poly_buf:
|
||||
self.app.inform.emit('[WARNING_NOTCL] %s' % _("Margin parameter too big. Tool is not used"))
|
||||
|
@ -2000,7 +2001,7 @@ class ToolPaint(AppTool, Gerber):
|
|||
prog_plot=prog_plot)
|
||||
geo_elems = list(geo_res.get_objects())
|
||||
# See if the polygon was completely cleared
|
||||
pp_cleared = cascaded_union(geo_elems).buffer(tool_dia / 2.0)
|
||||
pp_cleared = unary_union(geo_elems).buffer(tool_dia / 2.0)
|
||||
rest = pp.difference(pp_cleared)
|
||||
if rest and not rest.is_empty:
|
||||
try:
|
||||
|
@ -2040,7 +2041,7 @@ class ToolPaint(AppTool, Gerber):
|
|||
geo_elems = list(geo_res.get_objects())
|
||||
|
||||
# See if the polygon was completely cleared
|
||||
pp_cleared = cascaded_union(geo_elems).buffer(tool_dia / 2.0)
|
||||
pp_cleared = unary_union(geo_elems).buffer(tool_dia / 2.0)
|
||||
rest = poly_buf.difference(pp_cleared)
|
||||
if rest and not rest.is_empty:
|
||||
try:
|
||||
|
@ -2094,7 +2095,7 @@ class ToolPaint(AppTool, Gerber):
|
|||
|
||||
poly_buf = MultiPolygon(tmp)
|
||||
if not poly_buf.is_valid:
|
||||
poly_buf = cascaded_union(tmp)
|
||||
poly_buf = unary_union(tmp)
|
||||
|
||||
if not poly_buf or poly_buf.is_empty or not poly_buf.is_valid:
|
||||
log.debug("Rest geometry empty. Breaking.")
|
||||
|
@ -2134,7 +2135,7 @@ class ToolPaint(AppTool, Gerber):
|
|||
"Change the painting parameters and try again.")
|
||||
)
|
||||
return "fail"
|
||||
geo_obj.solid_geometry = cascaded_union(final_solid_geometry)
|
||||
geo_obj.solid_geometry = unary_union(final_solid_geometry)
|
||||
else:
|
||||
return 'fail'
|
||||
try:
|
||||
|
@ -2446,9 +2447,9 @@ class ToolPaint(AppTool, Gerber):
|
|||
env_obj = geo.convex_hull
|
||||
elif (isinstance(geo, MultiPolygon) and len(geo) == 1) or \
|
||||
(isinstance(geo, list) and len(geo) == 1) and isinstance(geo[0], Polygon):
|
||||
env_obj = cascaded_union(self.bound_obj.solid_geometry)
|
||||
env_obj = unary_union(self.bound_obj.solid_geometry)
|
||||
else:
|
||||
env_obj = cascaded_union(self.bound_obj.solid_geometry)
|
||||
env_obj = unary_union(self.bound_obj.solid_geometry)
|
||||
env_obj = env_obj.convex_hull
|
||||
sel_rect = env_obj.buffer(distance=0.0000001, join_style=base.JOIN_STYLE.mitre)
|
||||
except Exception as e:
|
||||
|
@ -2909,8 +2910,9 @@ class PaintUI:
|
|||
self.addtool_from_db_btn.setIcon(QtGui.QIcon(self.app.resource_location + '/search_db32.png'))
|
||||
self.addtool_from_db_btn.setToolTip(
|
||||
_("Add a new tool to the Tool Table\n"
|
||||
"from the Tool Database.\n"
|
||||
"Tool database administration in Menu: Options -> Tools Database")
|
||||
"from the Tools Database.\n"
|
||||
"Tools database administration in in:\n"
|
||||
"Menu: Options -> Tools Database")
|
||||
)
|
||||
hlay.addWidget(self.addtool_from_db_btn)
|
||||
|
||||
|
|
|
@ -589,8 +589,8 @@ class Panelize(AppTool):
|
|||
obj_fin.source_file = self.app.export_dxf(obj_name=self.outname, filename=None,
|
||||
local_use=obj_fin, use_thread=False)
|
||||
|
||||
# obj_fin.solid_geometry = cascaded_union(obj_fin.solid_geometry)
|
||||
# app_obj.log.debug("Finished creating a cascaded union for the panel.")
|
||||
# obj_fin.solid_geometry = unary_union(obj_fin.solid_geometry)
|
||||
# app_obj.log.debug("Finished creating a unary_union for the panel.")
|
||||
app_obj.proc_container.update_view_text('')
|
||||
|
||||
self.app.inform.emit('%s: %d' % (_("Generating panel... Spawning copies"), (int(rows * columns))))
|
||||
|
|
|
@ -10,7 +10,7 @@ from appTool import AppTool
|
|||
from appGUI.GUIElements import FCTree
|
||||
|
||||
from shapely.geometry import MultiPolygon, Polygon
|
||||
from shapely.ops import cascaded_union
|
||||
from shapely.ops import unary_union
|
||||
|
||||
from copy import deepcopy
|
||||
import math
|
||||
|
@ -237,7 +237,7 @@ class Properties(AppTool):
|
|||
if obj_prop.kind.lower() == 'cncjob':
|
||||
try:
|
||||
for tool_k in obj_prop.exc_cnc_tools:
|
||||
x0, y0, x1, y1 = cascaded_union(obj_prop.exc_cnc_tools[tool_k]['solid_geometry']).bounds
|
||||
x0, y0, x1, y1 = unary_union(obj_prop.exc_cnc_tools[tool_k]['solid_geometry']).bounds
|
||||
xmin.append(x0)
|
||||
ymin.append(y0)
|
||||
xmax.append(x1)
|
||||
|
@ -247,7 +247,7 @@ class Properties(AppTool):
|
|||
|
||||
try:
|
||||
for tool_k in obj_prop.cnc_tools:
|
||||
x0, y0, x1, y1 = cascaded_union(obj_prop.cnc_tools[tool_k]['solid_geometry']).bounds
|
||||
x0, y0, x1, y1 = unary_union(obj_prop.cnc_tools[tool_k]['solid_geometry']).bounds
|
||||
xmin.append(x0)
|
||||
ymin.append(y0)
|
||||
xmax.append(x1)
|
||||
|
@ -257,7 +257,7 @@ class Properties(AppTool):
|
|||
else:
|
||||
try:
|
||||
for tool_k in obj_prop.tools:
|
||||
x0, y0, x1, y1 = cascaded_union(obj_prop.tools[tool_k]['solid_geometry']).bounds
|
||||
x0, y0, x1, y1 = unary_union(obj_prop.tools[tool_k]['solid_geometry']).bounds
|
||||
xmin.append(x0)
|
||||
ymin.append(y0)
|
||||
xmax.append(x1)
|
||||
|
@ -308,10 +308,10 @@ class Properties(AppTool):
|
|||
env_obj = geo.convex_hull
|
||||
elif (isinstance(geo, MultiPolygon) and len(geo) == 1) or \
|
||||
(isinstance(geo, list) and len(geo) == 1) and isinstance(geo[0], Polygon):
|
||||
env_obj = cascaded_union(geo)
|
||||
env_obj = unary_union(geo)
|
||||
env_obj = env_obj.convex_hull
|
||||
else:
|
||||
env_obj = cascaded_union(geo)
|
||||
env_obj = unary_union(geo)
|
||||
env_obj = env_obj.convex_hull
|
||||
|
||||
area_chull = env_obj.area
|
||||
|
@ -321,7 +321,7 @@ class Properties(AppTool):
|
|||
try:
|
||||
area_chull = []
|
||||
for tool_k in obj_prop.tools:
|
||||
area_el = cascaded_union(obj_prop.tools[tool_k]['solid_geometry']).convex_hull
|
||||
area_el = unary_union(obj_prop.tools[tool_k]['solid_geometry']).convex_hull
|
||||
area_chull.append(area_el.area)
|
||||
area_chull = max(area_chull)
|
||||
except Exception as er:
|
||||
|
|
|
@ -430,7 +430,6 @@ class QRCode(AppTool):
|
|||
units = self.app.defaults['units'] if units is None else units
|
||||
res = self.app.defaults['geometry_circle_steps']
|
||||
factor = svgparse_viewbox(svg_root)
|
||||
|
||||
geos = getsvggeo(svg_root, object_type, units=units, res=res, factor=factor)
|
||||
|
||||
if flip:
|
||||
|
|
|
@ -19,7 +19,7 @@ from copy import deepcopy
|
|||
from datetime import datetime
|
||||
|
||||
from shapely.geometry import Polygon, LineString
|
||||
from shapely.ops import cascaded_union
|
||||
from shapely.ops import unary_union
|
||||
|
||||
import traceback
|
||||
from io import StringIO
|
||||
|
@ -941,7 +941,7 @@ class SolderPaste(AppTool):
|
|||
tool_cnc_dict['gcode_parsed'] = job_obj.gcode_parse()
|
||||
|
||||
# TODO this serve for bounding box creation only; should be optimized
|
||||
tool_cnc_dict['solid_geometry'] = cascaded_union([geo['geom'] for geo in tool_cnc_dict['gcode_parsed']])
|
||||
tool_cnc_dict['solid_geometry'] = unary_union([geo['geom'] for geo in tool_cnc_dict['gcode_parsed']])
|
||||
|
||||
# tell gcode_parse from which point to start drawing the lines depending on what kind of
|
||||
# object is the source of gcode
|
||||
|
|
|
@ -11,7 +11,7 @@ from appTool import AppTool
|
|||
from appGUI.GUIElements import FCCheckBox, FCButton, FCComboBox
|
||||
|
||||
from shapely.geometry import Polygon, MultiPolygon, MultiLineString, LineString
|
||||
from shapely.ops import cascaded_union
|
||||
from shapely.ops import unary_union
|
||||
|
||||
import traceback
|
||||
from copy import deepcopy
|
||||
|
@ -396,7 +396,7 @@ class ToolSub(AppTool):
|
|||
else:
|
||||
self.promises.append("single")
|
||||
|
||||
self.sub_union = cascaded_union(self.sub_geo_obj.solid_geometry)
|
||||
self.sub_union = unary_union(self.sub_geo_obj.solid_geometry)
|
||||
|
||||
# start the QTimer to check for promises with 0.5 second period check
|
||||
self.periodic_check(500, reset=True)
|
||||
|
@ -421,7 +421,7 @@ class ToolSub(AppTool):
|
|||
with self.app.proc_container.new(text):
|
||||
# resulting paths are closed resulting into Polygons
|
||||
if self.ui.close_paths_cb.isChecked():
|
||||
new_geo = (cascaded_union(geo)).difference(self.sub_union)
|
||||
new_geo = (unary_union(geo)).difference(self.sub_union)
|
||||
if new_geo:
|
||||
if not new_geo.is_empty:
|
||||
new_geometry.append(new_geo)
|
||||
|
|
69
app_Main.py
69
app_Main.py
|
@ -427,7 +427,7 @@ class App(QtCore.QObject):
|
|||
|
||||
current_defaults_path = os.path.join(self.data_path, "current_defaults.FlatConfig")
|
||||
if user_defaults:
|
||||
self.defaults.load(filename=current_defaults_path)
|
||||
self.defaults.load(filename=current_defaults_path, inform=self.inform)
|
||||
|
||||
if self.defaults['units'] == 'MM':
|
||||
self.decimals = int(self.defaults['decimals_metric'])
|
||||
|
@ -1092,7 +1092,7 @@ class App(QtCore.QObject):
|
|||
self.ui.splitter.setSizes([0, 1])
|
||||
|
||||
# Sets up FlatCAMObj, FCProcess and FCProcessContainer.
|
||||
self.setup_component_editor()
|
||||
self.setup_default_properties_tab()
|
||||
|
||||
# ###########################################################################################################
|
||||
# ####################################### Auto-complete KEYWORDS ############################################
|
||||
|
@ -2576,7 +2576,7 @@ class App(QtCore.QObject):
|
|||
return
|
||||
|
||||
# Load in the defaults from the chosen file
|
||||
self.defaults.load(filename=filename)
|
||||
self.defaults.load(filename=filename, inform=self.inform)
|
||||
|
||||
self.preferencesUiManager.on_preferences_edited()
|
||||
self.inform.emit('[success] %s: %s' % (_("Imported Defaults from"), filename))
|
||||
|
@ -4655,7 +4655,7 @@ class App(QtCore.QObject):
|
|||
self.collection.delete_active()
|
||||
|
||||
# Clear form
|
||||
self.setup_component_editor()
|
||||
self.setup_default_properties_tab()
|
||||
|
||||
self.inform.emit('%s: %s' % (_("Object deleted"), name))
|
||||
|
||||
|
@ -6965,13 +6965,13 @@ class App(QtCore.QObject):
|
|||
self.collection.delete_all()
|
||||
|
||||
# add in Selected tab an initial text that describe the flow of work in FlatCAm
|
||||
self.setup_component_editor()
|
||||
self.setup_default_properties_tab()
|
||||
|
||||
# Clear project filename
|
||||
self.project_filename = None
|
||||
|
||||
# Load the application defaults
|
||||
self.defaults.load(filename=os.path.join(self.data_path, 'current_defaults.FlatConfig'))
|
||||
self.defaults.load(filename=os.path.join(self.data_path, 'current_defaults.FlatConfig'), inform=self.inform)
|
||||
|
||||
# Re-fresh project options
|
||||
self.on_options_app2project()
|
||||
|
@ -9677,9 +9677,9 @@ class App(QtCore.QObject):
|
|||
|
||||
self.log.debug("Recent items list has been populated.")
|
||||
|
||||
def setup_component_editor(self):
|
||||
def setup_default_properties_tab(self):
|
||||
"""
|
||||
Default text for the Selected tab when is not taken by the Object UI.
|
||||
Default text for the Properties tab when is not taken by the Object UI.
|
||||
|
||||
:return:
|
||||
"""
|
||||
|
@ -9698,58 +9698,7 @@ class App(QtCore.QObject):
|
|||
|
||||
tsize = fsize + int(fsize / 2)
|
||||
|
||||
selected_text = '''
|
||||
<p><span style="font-size:{tsize}px"><strong>{title}</strong></span></p>
|
||||
|
||||
<p><span style="font-size:{fsize}px"><strong>{subtitle}</strong>:<br />
|
||||
{s1}</span></p>
|
||||
|
||||
<ol>
|
||||
<li><span style="font-size:{fsize}px">{s2}<br />
|
||||
<br />
|
||||
{s3}</span><br />
|
||||
</li>
|
||||
<li><span style="font-size:{fsize}px">{s4}<br />
|
||||
</li>
|
||||
<br />
|
||||
<li><span style="font-size:{fsize}px">{s5}<br />
|
||||
</li>
|
||||
<br />
|
||||
<li><span style="font-size:{fsize}px">{s6}<br />
|
||||
<br />
|
||||
{s7}</span></li>
|
||||
</ol>
|
||||
|
||||
<p><span style="font-size:{fsize}px">{s8}</span></p>
|
||||
'''.format(
|
||||
title=_("Properties Tab - Choose an Item from Project Tab"),
|
||||
subtitle=_("Details"),
|
||||
|
||||
s1=_("The normal flow when working with the application is the following:"),
|
||||
s2=_("Load/Import a Gerber, Excellon, Gcode, DXF, Raster Image or SVG file into the application "
|
||||
"using either the toolbars, key shortcuts or even dragging and dropping the "
|
||||
"files on the GUI."),
|
||||
s3=_("You can also load a project by double clicking on the project file, "
|
||||
"drag and drop of the file into the GUI or through the menu (or toolbar) "
|
||||
"actions offered within the app."),
|
||||
s4=_("Once an object is available in the Project Tab, by selecting it and then focusing "
|
||||
"on Properties TAB (more simpler is to double click the object name in the Project Tab, "
|
||||
"Properties TAB will be updated with the object properties according to its kind: "
|
||||
"Gerber, Excellon, Geometry or CNCJob object."),
|
||||
s5=_("If the selection of the object is done on the canvas by single click instead, "
|
||||
"and the Properties TAB is in focus, again the object properties will be displayed into the "
|
||||
"Properties Tab. Alternatively, double clicking on the object on the canvas will bring "
|
||||
"the Properties TAB and populate it even if it was out of focus."),
|
||||
s6=_("You can change the parameters in this screen and the flow direction is like this:"),
|
||||
s7=_("Gerber/Excellon Object --> Change Parameter --> Generate Geometry --> Geometry Object --> "
|
||||
"Add tools (change param in Selected Tab) --> Generate CNCJob --> CNCJob Object --> "
|
||||
"Verify GCode (through Edit CNC Code) and/or append/prepend to GCode "
|
||||
"(again, done in SELECTED TAB) --> Save GCode."),
|
||||
s8=_("A list of key shortcuts is available through an menu entry in Help --> Shortcuts List "
|
||||
"or through its own key shortcut: <b>F3</b>."),
|
||||
tsize=tsize,
|
||||
fsize=fsize
|
||||
)
|
||||
selected_text = ''
|
||||
|
||||
sel_title.setText(selected_text)
|
||||
sel_title.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)
|
||||
|
|
154
camlib.py
154
camlib.py
|
@ -25,7 +25,7 @@ from lxml import etree as ET
|
|||
from shapely.geometry import Polygon, Point, LinearRing
|
||||
|
||||
from shapely.geometry import box as shply_box
|
||||
from shapely.ops import cascaded_union, unary_union, substring, linemerge
|
||||
from shapely.ops import unary_union, substring, linemerge
|
||||
import shapely.affinity as affinity
|
||||
from shapely.wkt import loads as sloads
|
||||
from shapely.wkt import dumps as sdumps
|
||||
|
@ -225,13 +225,14 @@ class ApertureMacro:
|
|||
Pads the ``mods`` list with zeros resulting in an
|
||||
list of length n.
|
||||
|
||||
:param n: Length of the resulting list.
|
||||
:type n: int
|
||||
:param mods: List to be padded.
|
||||
:type mods: list
|
||||
:return: Zero-padded list.
|
||||
:rtype: list
|
||||
:param n: Length of the resulting list.
|
||||
:type n: int
|
||||
:param mods: List to be padded.
|
||||
:type mods: list
|
||||
:return: Zero-padded list.
|
||||
:rtype: list
|
||||
"""
|
||||
|
||||
x = [0.0] * n
|
||||
na = len(mods)
|
||||
x[0:na] = mods
|
||||
|
@ -244,9 +245,12 @@ class ApertureMacro:
|
|||
:param mods: (Exposure 0/1, Diameter >=0, X-coord, Y-coord)
|
||||
:return:
|
||||
"""
|
||||
|
||||
pol, dia, x, y = ApertureMacro.default2zero(4, mods)
|
||||
|
||||
val = ApertureMacro.default2zero(4, mods)
|
||||
pol = val[0]
|
||||
dia = val[1]
|
||||
x = val[2]
|
||||
y = val[3]
|
||||
# pol, dia, x, y = ApertureMacro.default2zero(4, mods)
|
||||
return {"pol": int(pol), "geometry": Point(x, y).buffer(dia / 2)}
|
||||
|
||||
@staticmethod
|
||||
|
@ -257,7 +261,15 @@ class ApertureMacro:
|
|||
rotation angle around origin in degrees)
|
||||
:return:
|
||||
"""
|
||||
pol, width, xs, ys, xe, ye, angle = ApertureMacro.default2zero(7, mods)
|
||||
val = ApertureMacro.default2zero(7, mods)
|
||||
pol = val[0]
|
||||
width = val[1]
|
||||
xs = val[2]
|
||||
ys = val[3]
|
||||
xe = val[4]
|
||||
ye = val[5]
|
||||
angle = val[6]
|
||||
# pol, width, xs, ys, xe, ye, angle = ApertureMacro.default2zero(7, mods)
|
||||
|
||||
line = LineString([(xs, ys), (xe, ye)])
|
||||
box = line.buffer(width / 2, cap_style=2)
|
||||
|
@ -274,7 +286,14 @@ class ApertureMacro:
|
|||
:return:
|
||||
"""
|
||||
|
||||
pol, width, height, x, y, angle = ApertureMacro.default2zero(6, mods)
|
||||
# pol, width, height, x, y, angle = ApertureMacro.default2zero(4, mods)
|
||||
val = ApertureMacro.default2zero(4, mods)
|
||||
pol = val[0]
|
||||
width = val[1]
|
||||
height = val[2]
|
||||
x = val[3]
|
||||
y = val[4]
|
||||
angle = val[5]
|
||||
|
||||
box = shply_box(x - width / 2, y - height / 2, x + width / 2, y + height / 2)
|
||||
box_rotated = affinity.rotate(box, angle, origin=(0, 0))
|
||||
|
@ -290,7 +309,14 @@ class ApertureMacro:
|
|||
:return:
|
||||
"""
|
||||
|
||||
pol, width, height, x, y, angle = ApertureMacro.default2zero(6, mods)
|
||||
# pol, width, height, x, y, angle = ApertureMacro.default2zero(6, mods)
|
||||
val = ApertureMacro.default2zero(6, mods)
|
||||
pol = val[0]
|
||||
width = val[1]
|
||||
height = val[2]
|
||||
x = val[3]
|
||||
y = val[4]
|
||||
angle = val[5]
|
||||
|
||||
box = shply_box(x, y, x + width, y + height)
|
||||
box_rotated = affinity.rotate(box, angle, origin=(0, 0))
|
||||
|
@ -330,7 +356,15 @@ class ApertureMacro:
|
|||
:return:
|
||||
"""
|
||||
|
||||
pol, nverts, x, y, dia, angle = ApertureMacro.default2zero(6, mods)
|
||||
# pol, nverts, x, y, dia, angle = ApertureMacro.default2zero(6, mods)
|
||||
val = ApertureMacro.default2zero(6, mods)
|
||||
pol = val[0]
|
||||
nverts = val[1]
|
||||
x = val[2]
|
||||
y = val[3]
|
||||
dia = val[4]
|
||||
angle = val[5]
|
||||
|
||||
points = [(0, 0)] * nverts
|
||||
|
||||
for i in range(nverts):
|
||||
|
@ -354,7 +388,17 @@ class ApertureMacro:
|
|||
:return:
|
||||
"""
|
||||
|
||||
x, y, dia, thickness, gap, nrings, cross_th, cross_len, angle = ApertureMacro.default2zero(9, mods)
|
||||
# x, y, dia, thickness, gap, nrings, cross_th, cross_len, angle = ApertureMacro.default2zero(9, mods)
|
||||
val = ApertureMacro.default2zero(9, mods)
|
||||
x = val[0]
|
||||
y = val[1]
|
||||
dia = val[2]
|
||||
thickness = val[3]
|
||||
gap = val[4]
|
||||
nrings = val[5]
|
||||
cross_th = val[6]
|
||||
cross_len = val[7]
|
||||
angle = val[8]
|
||||
|
||||
r = dia / 2 - thickness / 2
|
||||
result = Point((x, y)).buffer(r).exterior.buffer(thickness / 2.0)
|
||||
|
@ -369,13 +413,13 @@ class ApertureMacro:
|
|||
if r <= 0:
|
||||
break
|
||||
ring = Point((x, y)).buffer(r).exterior.buffer(thickness / 2.0)
|
||||
result = cascaded_union([result, ring])
|
||||
result = unary_union([result, ring])
|
||||
i += 1
|
||||
|
||||
# ## Crosshair
|
||||
hor = LineString([(x - cross_len, y), (x + cross_len, y)]).buffer(cross_th / 2.0, cap_style=2)
|
||||
ver = LineString([(x, y - cross_len), (x, y + cross_len)]).buffer(cross_th / 2.0, cap_style=2)
|
||||
result = cascaded_union([result, hor, ver])
|
||||
result = unary_union([result, hor, ver])
|
||||
|
||||
return {"pol": 1, "geometry": result}
|
||||
|
||||
|
@ -390,7 +434,14 @@ class ApertureMacro:
|
|||
:return:
|
||||
"""
|
||||
|
||||
x, y, dout, din, t, angle = ApertureMacro.default2zero(6, mods)
|
||||
# x, y, dout, din, t, angle = ApertureMacro.default2zero(6, mods)
|
||||
val = ApertureMacro.default2zero(6, mods)
|
||||
x = val[0]
|
||||
y = val[1]
|
||||
dout = val[2]
|
||||
din = val[3]
|
||||
t = val[4]
|
||||
angle = val[5]
|
||||
|
||||
ring = Point((x, y)).buffer(dout / 2.0).difference(Point((x, y)).buffer(din / 2.0))
|
||||
hline = LineString([(x - dout / 2.0, y), (x + dout / 2.0, y)]).buffer(t / 2.0, cap_style=3)
|
||||
|
@ -675,7 +726,7 @@ class Geometry(object):
|
|||
polygon = Polygon(points)
|
||||
else:
|
||||
polygon = points
|
||||
toolgeo = cascaded_union(polygon)
|
||||
toolgeo = unary_union(polygon)
|
||||
diffs = []
|
||||
for target in flat_geometry:
|
||||
if isinstance(target, LineString) or isinstance(target, LineString) or isinstance(target, MultiLineString):
|
||||
|
@ -787,7 +838,7 @@ class Geometry(object):
|
|||
# if len(self.solid_geometry) == 0:
|
||||
# log.debug('solid_geometry is empty []')
|
||||
# return 0, 0, 0, 0
|
||||
# return cascaded_union(flatten(self.solid_geometry)).bounds
|
||||
# return unary_union(flatten(self.solid_geometry)).bounds
|
||||
# else:
|
||||
# return self.solid_geometry.bounds
|
||||
# except Exception as e:
|
||||
|
@ -802,7 +853,7 @@ class Geometry(object):
|
|||
# if len(self.solid_geometry) == 0:
|
||||
# log.debug('solid_geometry is empty []')
|
||||
# return 0, 0, 0, 0
|
||||
# return cascaded_union(self.solid_geometry).bounds
|
||||
# return unary_union(self.solid_geometry).bounds
|
||||
# else:
|
||||
# return self.solid_geometry.bounds
|
||||
|
||||
|
@ -1325,7 +1376,7 @@ class Geometry(object):
|
|||
self.solid_geometry = []
|
||||
|
||||
if type(self.solid_geometry) is list:
|
||||
# self.solid_geometry.append(cascaded_union(geos))
|
||||
# self.solid_geometry.append(unary_union(geos))
|
||||
if type(geos) is list:
|
||||
self.solid_geometry += geos
|
||||
else:
|
||||
|
@ -1335,7 +1386,7 @@ class Geometry(object):
|
|||
|
||||
# flatten the self.solid_geometry list for import_svg() to import SVG as Gerber
|
||||
self.solid_geometry = list(self.flatten_list(self.solid_geometry))
|
||||
self.solid_geometry = cascaded_union(self.solid_geometry)
|
||||
self.solid_geometry = unary_union(self.solid_geometry)
|
||||
|
||||
# self.solid_geometry = MultiPolygon(self.solid_geometry)
|
||||
# self.solid_geometry = self.solid_geometry.buffer(0.00000001)
|
||||
|
@ -2211,17 +2262,17 @@ class Geometry(object):
|
|||
|
||||
def union(self):
|
||||
"""
|
||||
Runs a cascaded union on the list of objects in
|
||||
Runs a unary_union on the list of objects in
|
||||
solid_geometry.
|
||||
|
||||
:return: None
|
||||
"""
|
||||
self.solid_geometry = [cascaded_union(self.solid_geometry)]
|
||||
self.solid_geometry = [unary_union(self.solid_geometry)]
|
||||
|
||||
def export_svg(self, scale_stroke_factor=0.00,
|
||||
scale_factor_x=None, scale_factor_y=None,
|
||||
skew_factor_x=None, skew_factor_y=None,
|
||||
skew_reference='center',
|
||||
skew_reference='center', scale_reference='center',
|
||||
mirror=None):
|
||||
"""
|
||||
Exports the Geometry Object as a SVG Element
|
||||
|
@ -2235,11 +2286,11 @@ class Geometry(object):
|
|||
if self.multigeo:
|
||||
for tool in self.tools:
|
||||
flat_geo += self.flatten(self.tools[tool]['solid_geometry'])
|
||||
geom_svg = cascaded_union(flat_geo)
|
||||
geom_svg = unary_union(flat_geo)
|
||||
else:
|
||||
geom_svg = cascaded_union(self.flatten())
|
||||
geom_svg = unary_union(self.flatten())
|
||||
else:
|
||||
geom_svg = cascaded_union(self.flatten())
|
||||
geom_svg = unary_union(self.flatten())
|
||||
|
||||
skew_ref = 'center'
|
||||
if skew_reference != 'center':
|
||||
|
@ -2255,14 +2306,20 @@ class Geometry(object):
|
|||
|
||||
geom = geom_svg
|
||||
|
||||
if scale_factor_x:
|
||||
geom = affinity.scale(geom_svg, scale_factor_x, 1.0)
|
||||
if scale_factor_y:
|
||||
geom = affinity.scale(geom_svg, 1.0, scale_factor_y)
|
||||
if skew_factor_x:
|
||||
if scale_factor_x and not scale_factor_y:
|
||||
geom = affinity.scale(geom_svg, scale_factor_x, 1.0, origin=scale_reference)
|
||||
elif not scale_factor_x and scale_factor_y:
|
||||
geom = affinity.scale(geom_svg, 1.0, scale_factor_y, origin=scale_reference)
|
||||
elif scale_factor_x and scale_factor_y:
|
||||
geom = affinity.scale(geom_svg, scale_factor_x, scale_factor_y, origin=scale_reference)
|
||||
|
||||
if skew_factor_x and not skew_factor_y:
|
||||
geom = affinity.skew(geom_svg, skew_factor_x, 0.0, origin=skew_ref)
|
||||
if skew_factor_y:
|
||||
elif not skew_factor_x and skew_factor_y:
|
||||
geom = affinity.skew(geom_svg, 0.0, skew_factor_y, origin=skew_ref)
|
||||
elif skew_factor_x and skew_factor_y:
|
||||
geom = affinity.skew(geom_svg, skew_factor_x, skew_factor_y, origin=skew_ref)
|
||||
|
||||
if mirror:
|
||||
if mirror == 'x':
|
||||
geom = affinity.scale(geom_svg, 1.0, -1.0)
|
||||
|
@ -4818,7 +4875,7 @@ class CNCjob(Geometry):
|
|||
:return: GCode - string
|
||||
"""
|
||||
|
||||
log.debug("Generate_from_multitool_geometry()")
|
||||
log.debug("generate_from_multitool_geometry()")
|
||||
|
||||
temp_solid_geometry = []
|
||||
if offset != 0.0:
|
||||
|
@ -5128,7 +5185,7 @@ class CNCjob(Geometry):
|
|||
:rtype: str
|
||||
"""
|
||||
|
||||
log.debug("Generate_from_multitool_geometry()")
|
||||
log.debug("geometry_tool_gcode_gen()")
|
||||
|
||||
t_gcode = ''
|
||||
temp_solid_geometry = []
|
||||
|
@ -5244,7 +5301,11 @@ class CNCjob(Geometry):
|
|||
self.feedrate_rapid = float(tool_dict['feedrate_rapid'])
|
||||
|
||||
self.spindlespeed = float(tool_dict['spindlespeed'])
|
||||
self.spindledir = tool_dict['spindledir']
|
||||
try:
|
||||
self.spindledir = tool_dict['spindledir']
|
||||
except KeyError:
|
||||
self.spindledir = self.app.defaults["geometry_spindledir"]
|
||||
|
||||
self.dwell = tool_dict['dwell']
|
||||
self.dwelltime = float(tool_dict['dwelltime'])
|
||||
|
||||
|
@ -5253,8 +5314,9 @@ class CNCjob(Geometry):
|
|||
self.startz = None
|
||||
|
||||
self.z_end = float(tool_dict['endz'])
|
||||
self.xy_end = tool_dict['endxy']
|
||||
try:
|
||||
if self.xy_end == '':
|
||||
if self.xy_end == '' or self.xy_end is None:
|
||||
self.xy_end = None
|
||||
else:
|
||||
# either originally it was a string or not, xy_end will be made string
|
||||
|
@ -6807,7 +6869,7 @@ class CNCjob(Geometry):
|
|||
# This takes forever. Too much data?
|
||||
# self.app.inform.emit('%s: %s' % (_("Unifying Geometry from parsed Geometry segments"),
|
||||
# str(len(self.gcode_parsed))))
|
||||
# self.solid_geometry = cascaded_union([geo['geom'] for geo in self.gcode_parsed])
|
||||
# self.solid_geometry = unary_union([geo['geom'] for geo in self.gcode_parsed])
|
||||
|
||||
# This is much faster but not so nice to look at as you can see different segments of the geometry
|
||||
self.solid_geometry = [geo['geom'] for geo in self.gcode_parsed]
|
||||
|
@ -7413,18 +7475,18 @@ class CNCjob(Geometry):
|
|||
travels.append(g)
|
||||
|
||||
# Used to determine the overall board size
|
||||
self.solid_geometry = cascaded_union([geo['geom'] for geo in self.gcode_parsed])
|
||||
self.solid_geometry = unary_union([geo['geom'] for geo in self.gcode_parsed])
|
||||
|
||||
# Convert the cuts and travels into single geometry objects we can render as svg xml
|
||||
if travels:
|
||||
travelsgeom = cascaded_union([geo['geom'] for geo in travels])
|
||||
travelsgeom = unary_union([geo['geom'] for geo in travels])
|
||||
|
||||
if self.app.abort_flag:
|
||||
# graceful abort requested by the user
|
||||
raise grace
|
||||
|
||||
if cuts:
|
||||
cutsgeom = cascaded_union([geo['geom'] for geo in cuts])
|
||||
cutsgeom = unary_union([geo['geom'] for geo in cuts])
|
||||
|
||||
# Render the SVG Xml
|
||||
# The scale factor affects the size of the lines, and the stroke color adds different formatting for each set
|
||||
|
@ -7691,7 +7753,7 @@ class CNCjob(Geometry):
|
|||
self.app.proc_container.update_view_text(' %d%%' % disp_number)
|
||||
self.old_disp_number = disp_number
|
||||
|
||||
v['solid_geometry'] = cascaded_union([geo['geom'] for geo in v['gcode_parsed']])
|
||||
v['solid_geometry'] = unary_union([geo['geom'] for geo in v['gcode_parsed']])
|
||||
self.create_geometry()
|
||||
self.app.proc_container.new_text = ''
|
||||
|
||||
|
@ -7800,7 +7862,7 @@ class CNCjob(Geometry):
|
|||
self.old_disp_number = disp_number
|
||||
|
||||
# for the bounding box
|
||||
v['solid_geometry'] = cascaded_union([geo['geom'] for geo in v['gcode_parsed']])
|
||||
v['solid_geometry'] = unary_union([geo['geom'] for geo in v['gcode_parsed']])
|
||||
|
||||
self.app.proc_container.new_text = ''
|
||||
|
||||
|
@ -8172,7 +8234,7 @@ def dict2obj(d):
|
|||
#
|
||||
# m = MultiLineString(edge_points)
|
||||
# triangles = list(polygonize(m))
|
||||
# return cascaded_union(triangles), edge_points
|
||||
# return unary_union(triangles), edge_points
|
||||
|
||||
# def voronoi(P):
|
||||
# """
|
||||
|
|
20
defaults.py
20
defaults.py
|
@ -339,6 +339,11 @@ class FlatCAMDefaults:
|
|||
"geometry_area_shape": "polygon",
|
||||
"geometry_area_strategy": "over",
|
||||
"geometry_area_overz": 1.0,
|
||||
"geometry_polish": False,
|
||||
"geometry_polish_dia": 10.0,
|
||||
"geometry_polish_pressure": -1.0,
|
||||
"geometry_polish_overlap": 1.0,
|
||||
"geometry_polish_method": _("Standard"),
|
||||
|
||||
# Geometry Editor
|
||||
"geometry_editor_sel_limit": 30,
|
||||
|
@ -410,6 +415,7 @@ class FlatCAMDefaults:
|
|||
|
||||
"tools_iso_rest": False,
|
||||
"tools_iso_combine_passes": True,
|
||||
"tools_iso_check_valid": False,
|
||||
"tools_iso_isoexcept": False,
|
||||
"tools_iso_selection": _("All"),
|
||||
"tools_iso_poly_ints": False,
|
||||
|
@ -533,6 +539,7 @@ class FlatCAMDefaults:
|
|||
"tools_film_file_type_radio": 'svg',
|
||||
"tools_film_orientation": 'p',
|
||||
"tools_film_pagesize": 'A4',
|
||||
"tools_film_png_dpi": 96,
|
||||
|
||||
# Panel Tool
|
||||
"tools_panelize_spacing_columns": 0.0,
|
||||
|
@ -833,8 +840,13 @@ class FlatCAMDefaults:
|
|||
with open(filename, "w") as file:
|
||||
simplejson.dump(self.defaults, file, default=to_dict, indent=2, sort_keys=True)
|
||||
|
||||
def load(self, filename: str):
|
||||
"""Loads the defaults from a file on disk, performing migration if required."""
|
||||
def load(self, filename: str, inform):
|
||||
"""
|
||||
Loads the defaults from a file on disk, performing migration if required.
|
||||
|
||||
:param filename: a path to the file that is to be loaded
|
||||
:param inform: a pyqtSignal used to display information's in the StatusBar of the GUI
|
||||
"""
|
||||
|
||||
# Read in the file
|
||||
try:
|
||||
|
@ -843,7 +855,7 @@ class FlatCAMDefaults:
|
|||
f.close()
|
||||
except IOError:
|
||||
log.error("Could not load defaults file.")
|
||||
self.inform.emit('[ERROR] %s' % _("Could not load defaults file."))
|
||||
inform.emit('[ERROR] %s' % _("Could not load defaults file."))
|
||||
# in case the defaults file can't be loaded, show all toolbars
|
||||
self.defaults["global_toolbar_view"] = 511
|
||||
return
|
||||
|
@ -856,7 +868,7 @@ class FlatCAMDefaults:
|
|||
self.defaults["global_toolbar_view"] = 511
|
||||
e = sys.exc_info()[0]
|
||||
log.error(str(e))
|
||||
self.inform.emit('[ERROR] %s' % _("Failed to parse defaults file."))
|
||||
inform.emit('[ERROR] %s' % _("Failed to parse defaults file."))
|
||||
return
|
||||
if defaults is None:
|
||||
return
|
||||
|
|
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -1,7 +1,7 @@
|
|||
import collections
|
||||
from tclCommands.TclCommand import TclCommand
|
||||
|
||||
from shapely.ops import cascaded_union
|
||||
from shapely.ops import unary_union
|
||||
|
||||
import gettext
|
||||
import appTranslation as fcTranslate
|
||||
|
@ -94,7 +94,7 @@ class TclCommandBbox(TclCommand):
|
|||
# assert geo_obj.kind == 'geometry'
|
||||
|
||||
# Bounding box with rounded corners
|
||||
geo = cascaded_union(obj.solid_geometry)
|
||||
geo = unary_union(obj.solid_geometry)
|
||||
bounding_box = geo.envelope.buffer(float(margin))
|
||||
if not rounded: # Remove rounded corners
|
||||
bounding_box = bounding_box.envelope
|
||||
|
|
|
@ -3,7 +3,7 @@ from tclCommands.TclCommand import TclCommand
|
|||
import collections
|
||||
import logging
|
||||
|
||||
from shapely.ops import cascaded_union
|
||||
from shapely.ops import unary_union
|
||||
from shapely.geometry import LineString
|
||||
|
||||
log = logging.getLogger('base')
|
||||
|
@ -134,7 +134,7 @@ class TclCommandCutout(TclCommand):
|
|||
[pts[6], pts[7], pts[8]],
|
||||
[pts[9], pts[10], pts[11]]]}
|
||||
cuts = cases[gaps_par]
|
||||
geo_obj.solid_geometry = cascaded_union([LineString(segment) for segment in cuts])
|
||||
geo_obj.solid_geometry = unary_union([LineString(segment) for segment in cuts])
|
||||
|
||||
try:
|
||||
self.app.app_obj.new_object("geometry", outname, geo_init_me, plot=False)
|
||||
|
|
|
@ -21,12 +21,12 @@ class TclCommandExportSVG(TclCommand):
|
|||
arg_names = collections.OrderedDict([
|
||||
('name', str),
|
||||
('filename', str),
|
||||
('scale_factor', float)
|
||||
('scale_stroke_factor', float)
|
||||
])
|
||||
|
||||
# Dictionary of types from Tcl command, needs to be ordered , this is for options like -optionname value
|
||||
option_types = collections.OrderedDict([
|
||||
('scale_factor', float)
|
||||
('scale_stroke_factor', float)
|
||||
])
|
||||
|
||||
# array of mandatory options for current Tcl command: required = {'name','outname'}
|
||||
|
@ -39,7 +39,7 @@ class TclCommandExportSVG(TclCommand):
|
|||
('name', 'Name of the object export. Required.'),
|
||||
('filename', 'Absolute path to file to export.\n'
|
||||
'WARNING: no spaces are allowed. If unsure enclose the entire path with quotes.'),
|
||||
('scale_factor', 'Multiplication factor used for scaling line widths during export.')
|
||||
('scale_stroke_factor', 'Multiplication factor used for scaling line widths during export.')
|
||||
]),
|
||||
'examples': ['export_svg my_geometry my_file.svg']
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ from tclCommands.TclCommand import TclCommandSignaled
|
|||
import logging
|
||||
import collections
|
||||
from copy import deepcopy
|
||||
from shapely.ops import cascaded_union
|
||||
from shapely.ops import unary_union
|
||||
from shapely.geometry import Polygon, LineString, LinearRing
|
||||
|
||||
import gettext
|
||||
|
@ -131,14 +131,14 @@ class TclCommandGeoCutout(TclCommandSignaled):
|
|||
flat_geometry = flatten(geo, pathonly=True)
|
||||
|
||||
polygon = Polygon(pts)
|
||||
toolgeo = cascaded_union(polygon)
|
||||
toolgeo = unary_union(polygon)
|
||||
diffs = []
|
||||
for target in flat_geometry:
|
||||
if type(target) == LineString or type(target) == LinearRing:
|
||||
diffs.append(target.difference(toolgeo))
|
||||
else:
|
||||
log.warning("Not implemented.")
|
||||
return cascaded_union(diffs)
|
||||
return unary_union(diffs)
|
||||
|
||||
if 'name' in args:
|
||||
name = args['name']
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
from tclCommands.TclCommand import TclCommand
|
||||
|
||||
from shapely.ops import cascaded_union
|
||||
from shapely.ops import unary_union
|
||||
|
||||
import collections
|
||||
|
||||
|
@ -92,7 +92,7 @@ class TclCommandNregions(TclCommand):
|
|||
def geo_init(geo_obj, app_obj):
|
||||
assert geo_obj.kind == 'geometry'
|
||||
|
||||
geo = cascaded_union(obj.solid_geometry)
|
||||
geo = unary_union(obj.solid_geometry)
|
||||
bounding_box = geo.envelope.buffer(float(margin))
|
||||
if not rounded:
|
||||
bounding_box = bounding_box.envelope
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
from shapely.geometry import LineString, Polygon
|
||||
from shapely.ops import cascaded_union, unary_union
|
||||
from shapely.ops import unary_union
|
||||
from matplotlib.pyplot import plot, subplot, show, axes
|
||||
from matplotlib.axes import *
|
||||
from camlib import *
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import unittest
|
||||
|
||||
from shapely.geometry import LineString, Polygon
|
||||
from shapely.ops import cascaded_union, unary_union
|
||||
from shapely.ops import unary_union
|
||||
from matplotlib.pyplot import plot, subplot, show, cla, clf, xlim, ylim, title
|
||||
from matplotlib.axes import *
|
||||
from camlib import *
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import unittest
|
||||
|
||||
from shapely.geometry import LineString, Polygon
|
||||
from shapely.ops import cascaded_union, unary_union
|
||||
from shapely.ops import unary_union
|
||||
from matplotlib.pyplot import plot, subplot, show, cla, clf, xlim, ylim, title
|
||||
from camlib import *
|
||||
from random import random
|
||||
|
|
Loading…
Reference in New Issue