- fixed multiple issues in the App objects related to wrong usage of self.obj_options attribute instead of self.app.options attribute
- remade the Film Plugin such that the `skew` feature is now done in length units as opposed with the previous usage of angles - refactored some big methods from the Film Plugin
This commit is contained in:
@@ -12,6 +12,9 @@ CHANGELOG for FlatCAM beta
|
||||
- fixed an issue where using the 'G' shortcut key in Editors will not toggle the grid snap
|
||||
- fixed an issue in the Excellon Editor where selecting the drills did not highlight them but instead made them invisible (although the selection still worked)
|
||||
- fixed an issue in the Gerber Editor where selecting one shape will auto-select all the shapes made with the same aperture
|
||||
- fixed multiple issues in the App objects related to wrong usage of self.obj_options attribute instead of self.app.options attribute
|
||||
- remade the Film Plugin such that the `skew` feature is now done in length units as opposed with the previous usage of angles
|
||||
- refactored some big methods from the Film Plugin
|
||||
|
||||
9.03.2022
|
||||
|
||||
@@ -3454,7 +3457,7 @@ RELEASE 8.993
|
||||
|
||||
- working on a new tool: Extract Drills Tool who will create a Excellon object out of the apertures of a Gerber object
|
||||
- finished the GUI in the Extract Drills Tool
|
||||
- fixed issue in Film Tool where some parameters names in calls of method export_positive() were not matching the actual parameters name
|
||||
- fixed issue in Film Tool where some parameters names in calls of method export_positive_handler() were not matching the actual parameters name
|
||||
- finished the Extract Drills Tool
|
||||
- fixed a small issue in the DoubleSided Tool
|
||||
|
||||
|
||||
@@ -111,7 +111,7 @@ class ToolsFilmPrefGroupUI(OptionsGroupUI):
|
||||
grid_skew = FCGridLayout(v_spacing=5, h_spacing=3)
|
||||
skew_frame.setLayout(grid_skew)
|
||||
|
||||
self.film_skewx_label = FCLabel('%s:' % _("X angle"))
|
||||
self.film_skewx_label = FCLabel('%s:' % _("X val"))
|
||||
self.film_skewx_entry = FCDoubleSpinner()
|
||||
self.film_skewx_entry.set_range(-999.9999, 999.9999)
|
||||
self.film_skewx_entry.set_precision(self.decimals)
|
||||
@@ -120,7 +120,7 @@ class ToolsFilmPrefGroupUI(OptionsGroupUI):
|
||||
grid_skew.addWidget(self.film_skewx_label, 0, 0)
|
||||
grid_skew.addWidget(self.film_skewx_entry, 0, 1)
|
||||
|
||||
self.film_skewy_label = FCLabel('%s:' % _("Y angle"))
|
||||
self.film_skewy_label = FCLabel('%s:' % _("Y val"))
|
||||
self.film_skewy_entry = FCDoubleSpinner()
|
||||
self.film_skewy_entry.set_range(-999.9999, 999.9999)
|
||||
self.film_skewy_entry.set_precision(self.decimals)
|
||||
@@ -137,7 +137,7 @@ class ToolsFilmPrefGroupUI(OptionsGroupUI):
|
||||
|
||||
self.film_skew_ref_combo = FCComboBox2()
|
||||
self.film_skew_ref_combo.addItems(
|
||||
[_('Center'), _('Bottom Left'), _('Top Left'), _('Bottom Right'), _('Top right')])
|
||||
[_('Center'), _('Bottom Left')])
|
||||
|
||||
grid_skew.addWidget(self.skew_ref_label, 4, 0)
|
||||
grid_skew.addWidget(self.film_skew_ref_combo, 4, 1)
|
||||
|
||||
@@ -446,13 +446,13 @@ class AppObject(QtCore.QObject):
|
||||
for opt_key, opt_val in app.options.items():
|
||||
if opt_key.find('geometry' + "_") == 0:
|
||||
oname = opt_key[len('geometry') + 1:]
|
||||
default_data[oname] = self.app.options[opt_key]
|
||||
default_data[oname] = app.options[opt_key]
|
||||
if opt_key.find('tools_') == 0:
|
||||
default_data[opt_key] = self.app.options[opt_key]
|
||||
default_data[opt_key] = app.options[opt_key]
|
||||
|
||||
new_obj.tools = {
|
||||
1: {
|
||||
'tooldia': float(app.defaults["tools_mill_tooldia"]),
|
||||
'tooldia': float(app.options["tools_mill_tooldia"]),
|
||||
'offset': 'Path',
|
||||
'offset_value': 0.0,
|
||||
'type': 'Rough',
|
||||
|
||||
@@ -1020,7 +1020,7 @@ class ExcellonObject(FlatCAMObj, Excellon):
|
||||
|
||||
geo_obj.obj_options['type'] = 'Excellon Geometry'
|
||||
geo_obj.obj_options["tools_mill_tooldia"] = str(tooldia)
|
||||
geo_obj.obj_options["multidepth"] = app_obj.defaults["tools_mill_multidepth"]
|
||||
geo_obj.obj_options["multidepth"] = app_obj.options["tools_mill_multidepth"]
|
||||
geo_obj.solid_geometry = []
|
||||
|
||||
# in case that the tool used has the same diameter with the hole, and since the maximum resolution
|
||||
@@ -1117,7 +1117,7 @@ class ExcellonObject(FlatCAMObj, Excellon):
|
||||
|
||||
geo_obj.obj_options['type'] = 'Excellon Geometry'
|
||||
geo_obj.obj_options["tools_mill_tooldia"] = str(tooldia)
|
||||
geo_obj.obj_options["tools_mill_multidepth"] = app_obj.defaults["tools_mill_multidepth"]
|
||||
geo_obj.obj_options["tools_mill_multidepth"] = app_obj.options["tools_mill_multidepth"]
|
||||
geo_obj.solid_geometry = []
|
||||
|
||||
# in case that the tool used has the same diameter with the hole, and since the maximum resolution
|
||||
|
||||
@@ -585,12 +585,12 @@ class GerberObject(FlatCAMObj, Gerber):
|
||||
|
||||
# store here the default data for Geometry Data
|
||||
default_data = {}
|
||||
for opt_key, opt_val in app_obj.obj_options.items():
|
||||
for opt_key, opt_val in app_obj.options.items():
|
||||
if opt_key.find('geometry' + "_") == 0:
|
||||
oname = opt_key[len('geometry') + 1:]
|
||||
default_data[oname] = self.app.options[opt_key]
|
||||
default_data[oname] = app_obj.options[opt_key]
|
||||
if opt_key.find('tools_mill' + "_") == 0:
|
||||
default_data[opt_key] = self.app.options[opt_key]
|
||||
default_data[opt_key] = app_obj.options[opt_key]
|
||||
|
||||
geo_obj.tools = {
|
||||
1: {
|
||||
@@ -676,7 +676,7 @@ class GerberObject(FlatCAMObj, Gerber):
|
||||
def iso_init(geo_obj, app_obj):
|
||||
# Propagate options
|
||||
geo_obj.obj_options["tools_mill_tooldia"] = str(dia)
|
||||
geo_obj.tool_type = self.app.options["tools_iso_tool_shape"]
|
||||
geo_obj.tool_type = app_obj.options["tools_iso_tool_shape"]
|
||||
geo_obj.multigeo = True
|
||||
|
||||
# if milling type is climb then the move is counter-clockwise around features
|
||||
@@ -692,12 +692,12 @@ class GerberObject(FlatCAMObj, Gerber):
|
||||
|
||||
# store here the default data for Geometry Data
|
||||
default_data = {}
|
||||
for opt_key, opt_val in app_obj.obj_options.items():
|
||||
for opt_key, opt_val in app_obj.options.items():
|
||||
if opt_key.find('geometry' + "_") == 0:
|
||||
oname = opt_key[len('geometry') + 1:]
|
||||
default_data[oname] = self.app.options[opt_key]
|
||||
default_data[oname] = app_obj.options[opt_key]
|
||||
if opt_key.find('tools_mill' + "_") == 0:
|
||||
default_data[opt_key] = self.app.options[opt_key]
|
||||
default_data[opt_key] = app_obj.options[opt_key]
|
||||
|
||||
geo_obj.tools = {
|
||||
1: {
|
||||
@@ -786,7 +786,7 @@ class GerberObject(FlatCAMObj, Gerber):
|
||||
"""
|
||||
|
||||
if outname is None:
|
||||
follow_name = self.obj_options["name"] + "_follow"
|
||||
follow_name = self.options["name"] + "_follow"
|
||||
else:
|
||||
follow_name = outname
|
||||
|
||||
@@ -808,16 +808,16 @@ class GerberObject(FlatCAMObj, Gerber):
|
||||
# new_obj.obj_options["tools_mill_tooldia"] = str(self.app.options["tools_iso_tooldia"])
|
||||
new_obj.solid_geometry = deepcopy(self.follow_geometry)
|
||||
|
||||
new_obj.obj_options["tools_mill_tooldia"] = app_obj.defaults["tools_mill_tooldia"]
|
||||
new_obj.obj_options["tools_mill_tooldia"] = app_obj.options["tools_mill_tooldia"]
|
||||
|
||||
# store here the default data for Geometry Data
|
||||
default_data = {}
|
||||
for opt_key, opt_val in app_obj.obj_options.items():
|
||||
for opt_key, opt_val in app_obj.options.items():
|
||||
if opt_key.find('geometry' + "_") == 0:
|
||||
oname = opt_key[len('geometry') + 1:]
|
||||
default_data[oname] = self.app.options[opt_key]
|
||||
default_data[oname] = app_obj.options[opt_key]
|
||||
if opt_key.find('tools_mill' + "_") == 0:
|
||||
default_data[opt_key] = self.app.options[opt_key]
|
||||
default_data[opt_key] = app_obj.options[opt_key]
|
||||
|
||||
new_obj.tools = {
|
||||
1: {
|
||||
@@ -1021,10 +1021,7 @@ class GerberObject(FlatCAMObj, Gerber):
|
||||
# return
|
||||
|
||||
# for marking apertures, line color and fill color are the same
|
||||
if 'color' in kwargs:
|
||||
color = kwargs['color']
|
||||
else:
|
||||
color = self.app.options['gerber_plot_fill']
|
||||
color = kwargs['color'] if 'color' in kwargs else self.app.options['gerber_plot_fill']
|
||||
|
||||
if 'marked_aperture' in kwargs:
|
||||
aperture_to_plot_mark = kwargs['marked_aperture']
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
# Date: 3/10/2019 #
|
||||
# MIT Licence #
|
||||
# ##########################################################
|
||||
import math
|
||||
|
||||
from PyQt6 import QtCore, QtWidgets, QtGui
|
||||
|
||||
@@ -398,7 +399,7 @@ class Film(AppTool):
|
||||
filename = str(filename)
|
||||
|
||||
if str(filename) != "":
|
||||
self.export_positive(name, boxname, filename,
|
||||
self.export_positive_handler(name, boxname, filename,
|
||||
scale_stroke_factor=factor,
|
||||
scale_factor_x=scale_factor_x, scale_factor_y=scale_factor_y,
|
||||
scale_reference=scale_reference,
|
||||
@@ -553,7 +554,7 @@ class Film(AppTool):
|
||||
self.app.inform.emit('[WARNING_NOTCL] %s' % _("Cancelled."))
|
||||
return
|
||||
else:
|
||||
self.export_negative(name, boxname, filename, border,
|
||||
self.export_negative_handler(name, boxname, filename, border,
|
||||
scale_stroke_factor=factor,
|
||||
scale_factor_x=scale_factor_x, scale_factor_y=scale_factor_y,
|
||||
scale_reference=scale_reference,
|
||||
@@ -564,7 +565,7 @@ class Film(AppTool):
|
||||
rounded_box=rounded_box
|
||||
)
|
||||
|
||||
def export_negative(self, obj_name, box_name, filename, boundary,
|
||||
def export_negative_handler(self, obj_name, box_name, filename, boundary,
|
||||
scale_stroke_factor=0.00,
|
||||
scale_factor_x=1, scale_factor_y=1, scale_reference='center',
|
||||
skew_factor_x=None, skew_factor_y=None, skew_reference='center',
|
||||
@@ -596,7 +597,7 @@ class Film(AppTool):
|
||||
Works only in case the object used as box has multiple geometries
|
||||
:return:
|
||||
"""
|
||||
self.app.defaults.report_usage("export_negative()")
|
||||
self.app.defaults.report_usage("export_negative_handler()")
|
||||
|
||||
if filename is None:
|
||||
filename = self.app.options["global_last_save_folder"]
|
||||
@@ -620,30 +621,13 @@ class Film(AppTool):
|
||||
scale_factor_x = scale_factor_x
|
||||
scale_factor_y = scale_factor_y
|
||||
|
||||
def get_complementary(color_param):
|
||||
# strip the # from the beginning
|
||||
our_color = color_param[1:]
|
||||
|
||||
# convert the string into hex
|
||||
our_color = int(our_color, 16)
|
||||
|
||||
# invert the three bytes
|
||||
# as good as substracting each of RGB component by 255(FF)
|
||||
comp_color = 0xFFFFFF ^ our_color
|
||||
|
||||
# convert the color back to hex by prefixing a #
|
||||
comp_color = "#%06X" % comp_color
|
||||
|
||||
# return the result
|
||||
return comp_color
|
||||
|
||||
p_size = self.ui.pagesize_combo.get_value()
|
||||
orientation = self.ui.orientation_radio.get_value()
|
||||
color = obj.obj_options['tools_film_color']
|
||||
transparency_level = opacity_val
|
||||
|
||||
def make_negative_film(color, transparency_level, scale_factor_x, scale_factor_y, use_convex_hull, rounded_box):
|
||||
self.app.log.debug("FilmTool.export_negative().make_negative_film()")
|
||||
self.app.log.debug("FilmTool.export_negative_handler().make_negative_film()")
|
||||
|
||||
self.screen_dpi = self.app.qapp.screens()[0].logicalDotsPerInch()
|
||||
|
||||
@@ -654,85 +638,77 @@ class Film(AppTool):
|
||||
scale_factor_x += dpi_rate
|
||||
scale_factor_y += dpi_rate
|
||||
|
||||
# ########################################################################################################
|
||||
# the case when the BOX object is a Geometry Object
|
||||
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())
|
||||
|
||||
xmin, ymin, xmax, ymax = box_geo.bounds
|
||||
ref_scale_val = 'center'
|
||||
if scale_reference == 'topleft':
|
||||
ref_scale_val = (xmin, ymax)
|
||||
elif scale_reference == 'bottomleft':
|
||||
ref_scale_val = (xmin, ymin)
|
||||
elif scale_reference == 'topright':
|
||||
ref_scale_val = (xmax, ymax)
|
||||
elif scale_reference == 'bottomright':
|
||||
ref_scale_val = (xmax, ymin)
|
||||
|
||||
ref_skew_val = 'center'
|
||||
if skew_reference == 'topleft':
|
||||
ref_skew_val = (xmin, ymax)
|
||||
elif skew_reference == 'bottomleft':
|
||||
ref_skew_val = (xmin, ymin)
|
||||
elif skew_reference == 'topright':
|
||||
ref_skew_val = (xmax, ymax)
|
||||
elif skew_reference == 'bottomright':
|
||||
ref_skew_val = (xmax, ymin)
|
||||
|
||||
# Transform the box object geometry
|
||||
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=ref_scale_val)
|
||||
elif not scale_factor_x and scale_factor_y:
|
||||
transformed_box_geo = affinity.scale(transformed_box_geo, 1.0, scale_factor_y, origin=ref_scale_val)
|
||||
elif scale_factor_x and scale_factor_y:
|
||||
transformed_box_geo = affinity.scale(transformed_box_geo, scale_factor_x, scale_factor_y,
|
||||
origin=ref_scale_val)
|
||||
|
||||
if skew_factor_x and not skew_factor_y:
|
||||
transformed_box_geo = affinity.skew(transformed_box_geo, skew_factor_x, 0.0, origin=ref_skew_val)
|
||||
elif not skew_factor_x and skew_factor_y:
|
||||
transformed_box_geo = affinity.skew(transformed_box_geo, 0.0, skew_factor_y, origin=ref_skew_val)
|
||||
elif skew_factor_x and skew_factor_y:
|
||||
transformed_box_geo = affinity.skew(transformed_box_geo, skew_factor_x, skew_factor_y,
|
||||
origin=ref_skew_val)
|
||||
|
||||
if mirror:
|
||||
if mirror == 'x':
|
||||
transformed_box_geo = affinity.scale(transformed_box_geo, 1.0, -1.0, origin='center')
|
||||
if mirror == 'y':
|
||||
transformed_box_geo = affinity.scale(transformed_box_geo, -1.0, 1.0, origin='center')
|
||||
if mirror == 'both':
|
||||
transformed_box_geo = affinity.scale(transformed_box_geo, -1.0, -1.0, origin='center')
|
||||
|
||||
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,
|
||||
transformed_box_geo = self.transform_geometry(box, scale_factor_x=scale_factor_x,
|
||||
scale_factor_y=scale_factor_y,
|
||||
scale_reference=scale_reference,
|
||||
skew_factor_x=skew_factor_x, skew_factor_y=skew_factor_y,
|
||||
mirror=mirror,
|
||||
scale_reference=scale_reference, skew_reference=skew_reference,
|
||||
mirror_reference='center'
|
||||
)
|
||||
skew_reference=skew_reference,
|
||||
mirror=mirror)
|
||||
|
||||
uom = obj.units.lower()
|
||||
transformed_obj_geo = self.transform_geometry(obj, scale_factor_x=scale_factor_x,
|
||||
scale_factor_y=scale_factor_y,
|
||||
scale_reference=scale_reference,
|
||||
skew_factor_x=skew_factor_x, skew_factor_y=skew_factor_y,
|
||||
skew_reference=skew_reference,
|
||||
mirror=mirror)
|
||||
|
||||
exported_svg = self.create_svg_geometry(transformed_obj_geo, scale_stroke_factor=scale_stroke_factor)
|
||||
|
||||
svg_units = obj.units.lower()
|
||||
bounds = transformed_box_geo.bounds
|
||||
|
||||
doc_final = self.create_negative_svg(svg_geo=exported_svg, box_bounds=bounds, r_box=rounded_box,
|
||||
box_geo=transformed_box_geo, c_hull=use_convex_hull, margin=boundary,
|
||||
color=color, opacity=transparency_level, svg_units=svg_units)
|
||||
|
||||
obj_bounds = obj.bounds()
|
||||
ret = self.write_output_file(content2save=doc_final, filename=filename, file_type=ftype, p_size=p_size,
|
||||
orientation=orientation, source_bounds=obj_bounds, box_bounds=bounds)
|
||||
|
||||
if ret == 'fail':
|
||||
return 'fail'
|
||||
|
||||
if self.app.options["global_open_style"] is False:
|
||||
self.app.file_opened.emit("SVG", filename)
|
||||
self.app.file_saved.emit("SVG", filename)
|
||||
self.app.inform.emit('[success] %s: %s' % (_("Film file exported to"), filename))
|
||||
|
||||
if use_thread is True:
|
||||
def job_thread_film():
|
||||
with self.app.proc_container.new(_("Working...")):
|
||||
try:
|
||||
make_negative_film(color=color, transparency_level=transparency_level,
|
||||
scale_factor_x=scale_factor_x, scale_factor_y=scale_factor_y,
|
||||
use_convex_hull=use_convex_hull, rounded_box=rounded_box)
|
||||
except Exception as e:
|
||||
self.app.log.error("export_negative_handler() process -> %s" % str(e))
|
||||
return
|
||||
|
||||
self.app.worker_task.emit({'fcn': job_thread_film, 'params': []})
|
||||
else:
|
||||
make_negative_film(scale_factor_x=scale_factor_x, scale_factor_y=scale_factor_y,
|
||||
use_convex_hull=use_convex_hull, rounded_box=rounded_box)
|
||||
|
||||
def create_negative_svg(self, svg_geo, box_bounds, r_box, box_geo, c_hull, margin, color, opacity, svg_units):
|
||||
# Change the attributes of the exported SVG
|
||||
# We don't need stroke-width - wrong, we do when we have lines with certain width
|
||||
# We set opacity to maximum
|
||||
# We set the color to the inversed color
|
||||
root = ET.fromstring(svg_geo)
|
||||
for child in root:
|
||||
child.set('fill', self.get_complementary(color))
|
||||
child.set('opacity', str(opacity))
|
||||
child.set('stroke', self.get_complementary(color))
|
||||
|
||||
uom = svg_units
|
||||
|
||||
# Convert everything to strings for use in the xml doc
|
||||
svgwidth = str(size[0] + (2 * boundary))
|
||||
svgheight = str(size[1] + (2 * boundary))
|
||||
minx = str(bounds[0] - boundary)
|
||||
miny = str(bounds[1] + boundary + size[1])
|
||||
size = box_bounds[2] - box_bounds[0], box_bounds[3] - box_bounds[1]
|
||||
|
||||
svgwidth = str(size[0] + (2 * margin))
|
||||
svgheight = str(size[1] + (2 * margin))
|
||||
minx = str(box_bounds[0] - margin)
|
||||
miny = str(box_bounds[1] + margin + size[1])
|
||||
# miny_rect = str(bounds[1] - boundary)
|
||||
|
||||
# Add a SVG Header and footer to the svg output from shapely
|
||||
@@ -747,50 +723,24 @@ class Film(AppTool):
|
||||
svg_header += '<g transform="scale(1,-1)">'
|
||||
svg_footer = '</g> </svg>'
|
||||
|
||||
# Change the attributes of the exported SVG
|
||||
# We don't need stroke-width - wrong, we do when we have lines with certain width
|
||||
# We set opacity to maximum
|
||||
# We set the color to the inversed color
|
||||
root = ET.fromstring(exported_svg)
|
||||
for child in root:
|
||||
child.set('fill', get_complementary(color))
|
||||
child.set('opacity', str(transparency_level))
|
||||
child.set('stroke', get_complementary(color))
|
||||
|
||||
# first_svg_elem = 'rect x="' + minx + '" ' + 'y="' + miny_rect + '" '
|
||||
# first_svg_elem += 'width="' + svgwidth + '" ' + 'height="' + svgheight + '" '
|
||||
# first_svg_elem += 'fill="#000000" opacity="1.0" stroke-width="0.0"'
|
||||
|
||||
# first_svg_elem_tag = 'rect'
|
||||
# first_svg_elem_attribs = {
|
||||
# 'x': minx,
|
||||
# 'y': miny_rect,
|
||||
# 'width': svgwidth,
|
||||
# 'height': svgheight,
|
||||
# 'id': 'neg_rect',
|
||||
# 'style': 'fill:%s;opacity:1.0;stroke-width:0.0' % str(color)
|
||||
# }
|
||||
|
||||
# decide if to round the bounding box for the negative
|
||||
join_s = 1 if rounded_box else 2
|
||||
join_s = 1 if r_box else 2
|
||||
|
||||
if isinstance(transformed_box_geo, (LineString, LinearRing)):
|
||||
b_geo = Polygon(transformed_box_geo).buffer(boundary, join_style=join_s)
|
||||
if isinstance(box_geo, (LineString, LinearRing)):
|
||||
b_geo = Polygon(box_geo).buffer(margin, join_style=join_s)
|
||||
coords_list = list(b_geo.exterior.coords)
|
||||
elif isinstance(transformed_box_geo, list) and len(transformed_box_geo) == 1 and \
|
||||
isinstance(transformed_box_geo[0], (LineString, LinearRing)):
|
||||
b_geo = Polygon(transformed_box_geo[0]).buffer(boundary, join_style=join_s)
|
||||
elif isinstance(box_geo, list) and len(box_geo) == 1 and isinstance(box_geo[0], (LineString, LinearRing)):
|
||||
b_geo = Polygon(box_geo[0]).buffer(margin, join_style=join_s)
|
||||
coords_list = list(b_geo.exterior.coords)
|
||||
elif isinstance(transformed_box_geo, Polygon):
|
||||
coords_list = list(transformed_box_geo.exterior.coords)
|
||||
elif isinstance(transformed_box_geo, list) and len(transformed_box_geo) == 1 and \
|
||||
isinstance(transformed_box_geo[0], Polygon):
|
||||
coords_list = list(transformed_box_geo[0].exterior.coords)
|
||||
elif isinstance(box_geo, Polygon):
|
||||
coords_list = list(box_geo.exterior.coords)
|
||||
elif isinstance(box_geo, list) and len(box_geo) == 1 and isinstance(box_geo[0], Polygon):
|
||||
coords_list = list(box_geo[0].exterior.coords)
|
||||
else:
|
||||
if use_convex_hull:
|
||||
buff_box = transformed_box_geo.convex_hull.buffer(boundary, join_style=join_s)
|
||||
if c_hull:
|
||||
buff_box = box_geo.convex_hull.buffer(margin, join_style=join_s)
|
||||
else:
|
||||
buff_box = transformed_box_geo.envelope.buffer(boundary, join_style=join_s)
|
||||
buff_box = box_geo.envelope.buffer(margin, join_style=join_s)
|
||||
box_buff_outline = buff_box.exterior
|
||||
coords_list = list(box_buff_outline.coords)
|
||||
|
||||
@@ -813,111 +763,27 @@ class Film(AppTool):
|
||||
# Parse the xml through a xml parser just to add line feeds
|
||||
# and to make it look more pretty for the output
|
||||
doc = parse_xml_string(svg_elem)
|
||||
doc_final = doc.toprettyxml()
|
||||
return doc.toprettyxml()
|
||||
|
||||
if ftype == 'svg':
|
||||
try:
|
||||
with open(filename, 'w') as fp:
|
||||
fp.write(doc_final)
|
||||
except PermissionError:
|
||||
self.app.inform.emit('[ERROR_NOTCL] %s' %
|
||||
_("Permission denied, saving not possible.\n"
|
||||
"Most likely another app is holding the file open and not accessible."))
|
||||
return 'fail'
|
||||
elif ftype == 'png':
|
||||
try:
|
||||
doc_final = StringIO(doc_final)
|
||||
drawing = svg2rlg(doc_final)
|
||||
renderPM.drawToFile(drawing, filename, fmt='PNG')
|
||||
@staticmethod
|
||||
def get_complementary(color_param):
|
||||
# strip the # from the beginning
|
||||
our_color = color_param[1:]
|
||||
|
||||
# if new_png_dpi == default_dpi:
|
||||
# renderPM.drawToFile(drawing, filename, 'PNG')
|
||||
# else:
|
||||
# renderPM.drawToFile(drawing, filename, 'PNG', dpi=new_png_dpi)
|
||||
except PermissionError:
|
||||
self.app.inform.emit('[ERROR_NOTCL] %s' %
|
||||
_("Permission denied, saving not possible.\n"
|
||||
"Most likely another app is holding the file open and not accessible."))
|
||||
return 'fail'
|
||||
except Exception as e:
|
||||
self.app.log.error("FilmTool.export_negative() --> PNG output --> %s" % str(e))
|
||||
return 'fail'
|
||||
else: # PDF
|
||||
if self.units == 'IN':
|
||||
unit = inch
|
||||
else:
|
||||
unit = mm
|
||||
# convert the string into hex
|
||||
our_color = int(our_color, 16)
|
||||
|
||||
if p_size == 'Bounds':
|
||||
page_size = None
|
||||
elif orientation == 'p':
|
||||
page_size = portrait(self.ui.pagesize[p_size])
|
||||
else:
|
||||
page_size = landscape(self.ui.pagesize[p_size])
|
||||
# invert the three bytes
|
||||
# as good as substracting each of RGB component by 255(FF)
|
||||
comp_color = 0xFFFFFF ^ our_color
|
||||
|
||||
try:
|
||||
xmin, ymin, xmax, ymax = obj.bounds()
|
||||
if page_size:
|
||||
page_xmax, page_ymax = (
|
||||
page_size[0] / mm,
|
||||
page_size[1] / mm
|
||||
)
|
||||
else:
|
||||
page_xmax, page_ymax = xmax, ymax
|
||||
# convert the color back to hex by prefixing a #
|
||||
comp_color = "#%06X" % comp_color
|
||||
|
||||
if xmax < 0 or ymax < 0 or xmin > page_xmax or ymin > page_ymax:
|
||||
err_msg = '[ERROR_NOTCL] %s %s' % \
|
||||
(_("Failed."),
|
||||
_("The artwork has to be within the selected page size in order to be visible.\n"
|
||||
"For 'Bounds' page size, it needs to be in the first quadrant."))
|
||||
self.app.inform.emit(err_msg)
|
||||
return 'fail'
|
||||
except Exception as e:
|
||||
self.app.log.error("FilmTool.export_negative() --> PDF output 1 --> %s" % str(e))
|
||||
return 'fail'
|
||||
# return the result
|
||||
return comp_color
|
||||
|
||||
try:
|
||||
doc_final = StringIO(doc_final)
|
||||
drawing = svg2rlg(doc_final)
|
||||
|
||||
if p_size == 'Bounds':
|
||||
renderPDF.drawToFile(drawing, filename)
|
||||
else:
|
||||
my_canvas = canvas.Canvas(filename, pagesize=page_size)
|
||||
my_canvas.translate(bounds[0] * unit, bounds[1] * unit)
|
||||
renderPDF.draw(drawing, my_canvas, 0, 0)
|
||||
my_canvas.save()
|
||||
except PermissionError:
|
||||
self.app.inform.emit('[ERROR_NOTCL] %s' %
|
||||
_("Permission denied, saving not possible.\n"
|
||||
"Most likely another app is holding the file open and not accessible."))
|
||||
return 'fail'
|
||||
except Exception as e:
|
||||
self.app.log.error("FilmTool.export_negative() --> PDF output Reportlab section --> %s" % str(e))
|
||||
return 'fail'
|
||||
|
||||
if self.app.options["global_open_style"] is False:
|
||||
self.app.file_opened.emit("SVG", filename)
|
||||
self.app.file_saved.emit("SVG", filename)
|
||||
self.app.inform.emit('[success] %s: %s' % (_("Film file exported to"), filename))
|
||||
|
||||
if use_thread is True:
|
||||
def job_thread_film():
|
||||
with self.app.proc_container.new(_("Working...")):
|
||||
try:
|
||||
make_negative_film(color=color, transparency_level=transparency_level,
|
||||
scale_factor_x=scale_factor_x, scale_factor_y=scale_factor_y,
|
||||
use_convex_hull=use_convex_hull, rounded_box=rounded_box)
|
||||
except Exception as e:
|
||||
self.app.log.error("export_negative() process -> %s" % str(e))
|
||||
return
|
||||
|
||||
self.app.worker_task.emit({'fcn': job_thread_film, 'params': []})
|
||||
else:
|
||||
make_negative_film(scale_factor_x=scale_factor_x, scale_factor_y=scale_factor_y,
|
||||
use_convex_hull=use_convex_hull, rounded_box=rounded_box)
|
||||
|
||||
def export_positive(self, obj_name, box_name, filename,
|
||||
def export_positive_handler(self, obj_name, box_name, filename,
|
||||
scale_stroke_factor=0.00,
|
||||
scale_factor_x=1, scale_factor_y=1, scale_reference='center',
|
||||
skew_factor_x=None, skew_factor_y=None, skew_reference='center',
|
||||
@@ -938,7 +804,7 @@ class Film(AppTool):
|
||||
:param skew_factor_x: factor to skew the geometry on the X axis
|
||||
:param skew_factor_y: factor to skew the geometry on the Y axis
|
||||
:param skew_reference: reference to use for transformation.
|
||||
Values: 'center', 'bottomleft', 'topleft', 'bottomright', 'topright'
|
||||
Values: 'center', 'bottomleft'
|
||||
:param mirror: can be 'x' or 'y' or 'both'. Axis on which to mirror the svg geometry
|
||||
:param opacity_val:
|
||||
:param use_thread: if to be run in a separate thread; boolean
|
||||
@@ -946,12 +812,12 @@ class Film(AppTool):
|
||||
|
||||
:return:
|
||||
"""
|
||||
self.app.defaults.report_usage("export_positive()")
|
||||
self.app.defaults.report_usage("export_positive_handler()")
|
||||
|
||||
if filename is None:
|
||||
filename = self.app.options["global_last_save_folder"]
|
||||
|
||||
self.app.log.debug("Film.export_positive() black")
|
||||
self.app.log.debug("Film.export_positive_handler() black")
|
||||
|
||||
try:
|
||||
obj = self.app.collection.get_by_name(str(obj_name))
|
||||
@@ -964,7 +830,7 @@ class Film(AppTool):
|
||||
return "Could not retrieve object: %s" % box_name
|
||||
|
||||
if box is None:
|
||||
self.inform.emit('[WARNING_NOTCL] %s: %s' % (_("No object Box. Using instead"), obj))
|
||||
self.app.inform.emit('[WARNING_NOTCL] %s: %s' % (_("No object Box. Using instead"), obj))
|
||||
box = obj
|
||||
|
||||
scale_factor_x = scale_factor_x
|
||||
@@ -976,7 +842,7 @@ class Film(AppTool):
|
||||
transparency_level = opacity_val
|
||||
|
||||
def make_positive_film(color, transparency_level, scale_factor_x, scale_factor_y):
|
||||
self.app.log.debug("FilmTool.export_positive().make_positive_film()")
|
||||
self.app.log.debug("FilmTool.export_positive_handler().make_positive_film()")
|
||||
|
||||
self.screen_dpi = self.app.qapp.screens()[0].logicalDotsPerInch()
|
||||
|
||||
@@ -987,98 +853,81 @@ class Film(AppTool):
|
||||
scale_factor_x += dpi_rate
|
||||
scale_factor_y += dpi_rate
|
||||
|
||||
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())
|
||||
transformed_box_geo = self.transform_geometry(box, scale_factor_x=scale_factor_x,
|
||||
scale_factor_y=scale_factor_y,
|
||||
scale_reference=scale_reference,
|
||||
skew_factor_x=skew_factor_x, skew_factor_y=skew_factor_y,
|
||||
skew_reference=skew_reference,
|
||||
mirror=mirror)
|
||||
|
||||
xmin, ymin, xmax, ymax = box_geo.bounds
|
||||
ref_scale_val = 'center'
|
||||
if scale_reference == 'topleft':
|
||||
ref_scale_val = (xmin, ymax)
|
||||
elif scale_reference == 'bottomleft':
|
||||
ref_scale_val = (xmin, ymin)
|
||||
elif scale_reference == 'topright':
|
||||
ref_scale_val = (xmax, ymax)
|
||||
elif scale_reference == 'bottomright':
|
||||
ref_scale_val = (xmax, ymin)
|
||||
transformed_obj_geo = self.transform_geometry(obj, scale_factor_x=scale_factor_x,
|
||||
scale_factor_y=scale_factor_y,
|
||||
scale_reference=scale_reference,
|
||||
skew_factor_x=skew_factor_x, skew_factor_y=skew_factor_y,
|
||||
skew_reference=skew_reference,
|
||||
mirror=mirror)
|
||||
|
||||
ref_skew_val = 'center'
|
||||
if skew_reference == 'topleft':
|
||||
ref_skew_val = (xmin, ymax)
|
||||
elif skew_reference == 'bottomleft':
|
||||
ref_skew_val = (xmin, ymin)
|
||||
elif skew_reference == 'topright':
|
||||
ref_skew_val = (xmax, ymax)
|
||||
elif skew_reference == 'bottomright':
|
||||
ref_skew_val = (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=ref_scale_val)
|
||||
elif not scale_factor_x and scale_factor_y:
|
||||
transformed_box_geo = affinity.scale(transformed_box_geo, 1.0, scale_factor_y, origin=ref_scale_val)
|
||||
elif scale_factor_x and scale_factor_y:
|
||||
transformed_box_geo = affinity.scale(transformed_box_geo, scale_factor_x, scale_factor_y,
|
||||
origin=ref_scale_val)
|
||||
|
||||
if skew_factor_x and not skew_factor_y:
|
||||
transformed_box_geo = affinity.skew(transformed_box_geo, skew_factor_x, 0.0, origin=ref_skew_val)
|
||||
elif not skew_factor_x and skew_factor_y:
|
||||
transformed_box_geo = affinity.skew(transformed_box_geo, 0.0, skew_factor_y, origin=ref_skew_val)
|
||||
elif skew_factor_x and skew_factor_y:
|
||||
transformed_box_geo = affinity.skew(transformed_box_geo, skew_factor_x, skew_factor_y,
|
||||
origin=ref_skew_val)
|
||||
|
||||
if mirror:
|
||||
if mirror == 'x':
|
||||
transformed_box_geo = affinity.scale(transformed_box_geo, 1.0, -1.0, origin='center')
|
||||
if mirror == 'y':
|
||||
transformed_box_geo = affinity.scale(transformed_box_geo, -1.0, 1.0, origin='center')
|
||||
if mirror == 'both':
|
||||
transformed_box_geo = affinity.scale(transformed_box_geo, -1.0, -1.0, origin='center')
|
||||
exported_svg = self.create_svg_geometry(transformed_obj_geo, scale_stroke_factor=scale_stroke_factor)
|
||||
|
||||
bounds = transformed_box_geo.bounds
|
||||
size = bounds[2] - bounds[0], bounds[3] - bounds[1]
|
||||
svg_units = obj.units.lower()
|
||||
# Define a boundary around SVG
|
||||
margin = self.ui.boundary_entry.get_value()
|
||||
|
||||
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,
|
||||
scale_reference=scale_reference, skew_reference=skew_reference,
|
||||
mirror_reference='center'
|
||||
)
|
||||
doc_final = self.create_positive_svg(svg_geo=exported_svg, box_bounds=bounds, margin=margin, color=color,
|
||||
opacity=transparency_level, svg_units=svg_units)
|
||||
|
||||
obj_bounds = obj.bounds()
|
||||
ret = self.write_output_file(content2save=doc_final, filename=filename, file_type=ftype, p_size=p_size,
|
||||
orientation=orientation, source_bounds=obj_bounds, box_bounds=bounds)
|
||||
|
||||
if ret == 'fail':
|
||||
return 'fail'
|
||||
|
||||
if self.app.options["global_open_style"] is False:
|
||||
self.app.file_opened.emit("SVG", filename)
|
||||
self.app.file_saved.emit("SVG", filename)
|
||||
self.app.inform.emit('[success] %s: %s' % (_("Film file exported to"), filename))
|
||||
|
||||
if use_thread is True:
|
||||
def job_thread_film():
|
||||
with self.app.proc_container.new(_("Working...")):
|
||||
try:
|
||||
make_positive_film(color=color, transparency_level=transparency_level,
|
||||
scale_factor_x=scale_factor_x, scale_factor_y=scale_factor_y)
|
||||
except Exception as e:
|
||||
self.app.log.error("export_positive_handler() process -> %s" % str(e))
|
||||
return
|
||||
|
||||
self.app.worker_task.emit({'fcn': job_thread_film, 'params': []})
|
||||
else:
|
||||
make_positive_film(color=color, transparency_level=transparency_level,
|
||||
scale_factor_x=scale_factor_x, scale_factor_y=scale_factor_y)
|
||||
|
||||
@staticmethod
|
||||
def create_positive_svg(svg_geo, box_bounds, margin, color, opacity, svg_units):
|
||||
# Change the attributes of the exported SVG
|
||||
# We don't need stroke-width
|
||||
# We set opacity to maximum
|
||||
# We set the colour to WHITE
|
||||
root = ET.fromstring(exported_svg)
|
||||
root = ET.fromstring(svg_geo)
|
||||
for child in root:
|
||||
child.set('fill', str(color))
|
||||
child.set('opacity', str(transparency_level))
|
||||
child.set('opacity', str(opacity))
|
||||
child.set('stroke', str(color))
|
||||
|
||||
exported_svg = ET.tostring(root)
|
||||
|
||||
# This contain the measure units
|
||||
uom = obj.units.lower()
|
||||
|
||||
# Define a boundary around SVG
|
||||
boundary = self.ui.boundary_entry.get_value()
|
||||
uom = svg_units
|
||||
|
||||
# Convert everything to strings for use in the xml doc
|
||||
svgwidth = str(size[0] + (2 * boundary))
|
||||
svgheight = str(size[1] + (2 * boundary))
|
||||
minx = str(bounds[0] - boundary)
|
||||
miny = str(bounds[1] + boundary + size[1])
|
||||
size = box_bounds[2] - box_bounds[0], box_bounds[3] - box_bounds[1]
|
||||
|
||||
svgwidth = str(size[0] + (2 * margin))
|
||||
svgheight = str(size[1] + (2 * margin))
|
||||
minx = str(box_bounds[0] - margin)
|
||||
miny = str(box_bounds[1] + margin + size[1])
|
||||
|
||||
# Add a SVG Header and footer to the svg output from shapely
|
||||
# The transform flips the Y Axis so that everything renders
|
||||
@@ -1097,29 +946,28 @@ class Film(AppTool):
|
||||
# Parse the xml through a xml parser just to add line feeds
|
||||
# and to make it look more pretty for the output
|
||||
doc = parse_xml_string(svg_elem)
|
||||
doc_final = doc.toprettyxml()
|
||||
return doc.toprettyxml()
|
||||
|
||||
if ftype == 'svg':
|
||||
def write_output_file(self, content2save, filename, file_type, p_size, orientation, source_bounds, box_bounds):
|
||||
p_msg = '[ERROR_NOTCL] %s' % _("Permission denied, saving not possible.\n"
|
||||
"Most likely another app is holding the file open and not accessible.")
|
||||
if file_type == 'svg':
|
||||
try:
|
||||
with open(filename, 'w') as fp:
|
||||
fp.write(doc_final)
|
||||
fp.write(content2save)
|
||||
except PermissionError:
|
||||
self.app.inform.emit('[ERROR_NOTCL] %s' %
|
||||
_("Permission denied, saving not possible.\n"
|
||||
"Most likely another app is holding the file open and not accessible."))
|
||||
self.app.inform.emit(p_msg)
|
||||
return 'fail'
|
||||
elif ftype == 'png':
|
||||
elif file_type == 'png':
|
||||
try:
|
||||
doc_final = StringIO(doc_final)
|
||||
doc_final = StringIO(content2save)
|
||||
drawing = svg2rlg(doc_final)
|
||||
renderPM.drawToFile(drawing, filename, 'PNG')
|
||||
except PermissionError:
|
||||
self.app.inform.emit('[ERROR_NOTCL] %s' %
|
||||
_("Permission denied, saving not possible.\n"
|
||||
"Most likely another app is holding the file open and not accessible."))
|
||||
self.app.inform.emit(p_msg)
|
||||
return 'fail'
|
||||
except Exception as e:
|
||||
self.app.log.error("FilmTool.export_positive() --> PNG output --> %s" % str(e))
|
||||
self.app.log.error("FilmTool.write_output_file() --> PNG output --> %s" % str(e))
|
||||
return 'fail'
|
||||
else: # PDF
|
||||
try:
|
||||
@@ -1135,7 +983,7 @@ class Film(AppTool):
|
||||
else:
|
||||
page_size = landscape(self.ui.pagesize[p_size])
|
||||
|
||||
xmin, ymin, xmax, ymax = obj.bounds()
|
||||
xmin, ymin, xmax, ymax = source_bounds
|
||||
if page_size:
|
||||
page_xmax, page_ymax = (
|
||||
page_size[0] / mm,
|
||||
@@ -1146,50 +994,138 @@ class Film(AppTool):
|
||||
|
||||
if xmax < 0 or ymax < 0 or xmin > page_xmax or ymin > page_ymax:
|
||||
err_msg = '[ERROR_NOTCL] %s %s' % \
|
||||
(_("Failed."),
|
||||
(
|
||||
_("Failed."),
|
||||
_("The artwork has to be within the selected page size in order to be visible.\n"
|
||||
"For 'Bounds' page size, it needs to be in the first quadrant."))
|
||||
"For 'Bounds' page size, it needs to be in the first quadrant.")
|
||||
)
|
||||
self.app.inform.emit(err_msg)
|
||||
return 'fail'
|
||||
|
||||
doc_final = StringIO(doc_final)
|
||||
doc_final = StringIO(content2save)
|
||||
drawing = svg2rlg(doc_final)
|
||||
|
||||
if p_size == 'Bounds':
|
||||
renderPDF.drawToFile(drawing, filename)
|
||||
else:
|
||||
my_canvas = canvas.Canvas(filename, pagesize=page_size)
|
||||
my_canvas.translate(bounds[0] * unit, bounds[1] * unit)
|
||||
my_canvas.translate(box_bounds[0] * unit, box_bounds[1] * unit)
|
||||
renderPDF.draw(drawing, my_canvas, 0, 0)
|
||||
my_canvas.save()
|
||||
except PermissionError:
|
||||
self.app.inform.emit('[ERROR_NOTCL] %s' %
|
||||
_("Permission denied, saving not possible.\n"
|
||||
"Most likely another app is holding the file open and not accessible."))
|
||||
self.app.inform.emit(p_msg)
|
||||
return 'fail'
|
||||
except Exception as e:
|
||||
self.app.log.error("FilmTool.export_positive() --> PDF output --> %s" % str(e))
|
||||
self.app.log.error("FilmTool.write_output_file() --> PDF output --> %s" % str(e))
|
||||
return 'fail'
|
||||
|
||||
if self.app.options["global_open_style"] is False:
|
||||
self.app.file_opened.emit("SVG", filename)
|
||||
self.app.file_saved.emit("SVG", filename)
|
||||
self.app.inform.emit('[success] %s: %s' % (_("Film file exported to"), filename))
|
||||
@staticmethod
|
||||
def transform_geometry(obj, scale_factor_x=None, scale_factor_y=None,
|
||||
skew_factor_x=None, skew_factor_y=None,
|
||||
skew_reference='center', scale_reference='center', mirror=None):
|
||||
"""
|
||||
Return a transformed geometry made from a Shapely geometry collection property of the `obj` object
|
||||
|
||||
if use_thread is True:
|
||||
def job_thread_film():
|
||||
with self.app.proc_container.new(_("Working...")):
|
||||
try:
|
||||
make_positive_film(color=color, transparency_level=transparency_level,
|
||||
scale_factor_x=scale_factor_x, scale_factor_y=scale_factor_y)
|
||||
except Exception as e:
|
||||
self.app.log.error("export_positive() process -> %s" % str(e))
|
||||
return
|
||||
:return: Shapely geometry transformed
|
||||
"""
|
||||
|
||||
self.app.worker_task.emit({'fcn': job_thread_film, 'params': []})
|
||||
# Make sure we see a Shapely Geometry class and not a list
|
||||
if obj.kind.lower() == 'geometry':
|
||||
flat_geo = []
|
||||
if obj.multigeo:
|
||||
for tool in obj.tools:
|
||||
flat_geo += obj.flatten(obj.tools[tool]['solid_geometry'])
|
||||
transformed_geo = unary_union(flat_geo)
|
||||
else:
|
||||
make_positive_film(color=color, transparency_level=transparency_level,
|
||||
scale_factor_x=scale_factor_x, scale_factor_y=scale_factor_y)
|
||||
transformed_geo = unary_union(obj.flatten())
|
||||
else:
|
||||
transformed_geo = unary_union(obj.flatten())
|
||||
|
||||
# SCALING
|
||||
if scale_factor_x or scale_factor_y:
|
||||
xmin, ymin, xmax, ymax = transformed_geo.bounds
|
||||
ref_scale_val = 'center'
|
||||
if scale_reference == 'topleft':
|
||||
ref_scale_val = (xmin, ymax)
|
||||
elif scale_reference == 'bottomleft':
|
||||
ref_scale_val = (xmin, ymin)
|
||||
elif scale_reference == 'topright':
|
||||
ref_scale_val = (xmax, ymax)
|
||||
elif scale_reference == 'bottomright':
|
||||
ref_scale_val = (xmax, ymin)
|
||||
|
||||
if scale_factor_x and not scale_factor_y:
|
||||
val_x = scale_factor_x
|
||||
val_y = 0
|
||||
elif not scale_factor_x and scale_factor_y:
|
||||
val_x = 0
|
||||
val_y = scale_factor_y
|
||||
else:
|
||||
val_x = scale_factor_x
|
||||
val_y = scale_factor_y
|
||||
transformed_geo = affinity.scale(transformed_geo, val_x, val_y, origin=ref_scale_val)
|
||||
|
||||
# SKEWING
|
||||
if skew_factor_x or skew_factor_y:
|
||||
xmin, ymin, xmax, ymax = transformed_geo.bounds
|
||||
if skew_reference == 'bottomleft':
|
||||
ref_skew_val = (xmin, ymin)
|
||||
if skew_factor_x and not skew_factor_y:
|
||||
skew_angle_x = math.degrees(math.atan2(skew_factor_x, (ymax - ymin)))
|
||||
skew_angle_y = 0.0
|
||||
elif not skew_factor_x and skew_factor_y:
|
||||
skew_angle_x = 0.0
|
||||
skew_angle_y = math.degrees(math.atan2(skew_factor_y, (xmax - xmin)))
|
||||
else:
|
||||
skew_angle_x = math.degrees(math.atan2(skew_factor_x, (ymax - ymin)))
|
||||
skew_angle_y = math.degrees(math.atan2(skew_factor_y, (xmax - xmin)))
|
||||
else:
|
||||
ref_skew_val = 'center'
|
||||
if skew_factor_x and not skew_factor_y:
|
||||
skew_angle_x = math.degrees(math.atan2(skew_factor_x, ((ymax - ymin) * 0.5)))
|
||||
skew_angle_y = 0.0
|
||||
elif not skew_factor_x and skew_factor_y:
|
||||
skew_angle_x = 0.0
|
||||
skew_angle_y = math.degrees(math.atan2(skew_factor_y, ((xmax - xmin) * 0.5)))
|
||||
else:
|
||||
skew_angle_x = math.degrees(math.atan2(skew_factor_x, ((ymax - ymin) * 0.5)))
|
||||
skew_angle_y = math.degrees(math.atan2(skew_factor_y, ((xmax - xmin) * 0.5)))
|
||||
|
||||
transformed_geo = affinity.skew(transformed_geo, skew_angle_x, skew_angle_y, origin=ref_skew_val)
|
||||
|
||||
if mirror:
|
||||
if mirror == 'x':
|
||||
transformed_geo = affinity.scale(transformed_geo, 1.0, -1.0, origin='center')
|
||||
if mirror == 'y':
|
||||
transformed_geo = affinity.scale(transformed_geo, -1.0, 1.0, origin='center')
|
||||
if mirror == 'both':
|
||||
transformed_geo = affinity.scale(transformed_geo, -1.0, -1.0, origin='center')
|
||||
|
||||
return transformed_geo
|
||||
|
||||
@staticmethod
|
||||
def create_svg_geometry(geom, scale_stroke_factor):
|
||||
"""
|
||||
Return SVG geometry made from a Shapely geometry collection property of the `obj` object
|
||||
|
||||
:param geom: Shapely geometry collection
|
||||
:type geom:
|
||||
:param scale_stroke_factor: multiplication factor for the SVG stroke-width used within shapely's svg export
|
||||
If 0 or less which is invalid then default to 0.01
|
||||
This value appears to work for zooming, and getting the output svg line width
|
||||
to match that viewed on screen with FlatCam
|
||||
MS: I choose a factor of 0.01 so the scale is right for PCB UV film
|
||||
:type scale_stroke_factor: float
|
||||
:return: SVG geometry
|
||||
:rtype:
|
||||
"""
|
||||
|
||||
if scale_stroke_factor <= 0:
|
||||
scale_stroke_factor = 0.01
|
||||
|
||||
# Convert to a SVG
|
||||
svg_elem = geom.svg(scale_factor=scale_stroke_factor)
|
||||
return svg_elem
|
||||
|
||||
def reset_fields(self):
|
||||
self.ui.tf_object_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
|
||||
@@ -1399,7 +1335,7 @@ class FilmUI:
|
||||
adj_grid.addWidget(self.film_skew_cb, 12, 0, 1, 2)
|
||||
|
||||
# Skew X
|
||||
self.film_skewx_label = FCLabel('%s:' % _("X angle"))
|
||||
self.film_skewx_label = FCLabel('%s:' % _("X val"))
|
||||
self.film_skewx_entry = FCDoubleSpinner(callback=self.confirmation_message)
|
||||
self.film_skewx_entry.set_range(-999.9999, 999.9999)
|
||||
self.film_skewx_entry.set_precision(self.decimals)
|
||||
@@ -1409,7 +1345,7 @@ class FilmUI:
|
||||
adj_grid.addWidget(self.film_skewx_entry, 14, 1)
|
||||
|
||||
# Skew Y
|
||||
self.film_skewy_label = FCLabel('%s:' % _("Y angle"))
|
||||
self.film_skewy_label = FCLabel('%s:' % _("Y val"))
|
||||
self.film_skewy_entry = FCDoubleSpinner(callback=self.confirmation_message)
|
||||
self.film_skewy_entry.set_range(-999.9999, 999.9999)
|
||||
self.film_skewy_entry.set_precision(self.decimals)
|
||||
@@ -1426,7 +1362,7 @@ class FilmUI:
|
||||
|
||||
self.skew_ref_combo = FCComboBox2()
|
||||
self.skew_ref_combo.addItems(
|
||||
[_('Center'), _('Bottom Left'), _('Top Left'), _('Bottom Right'), _('Top right')])
|
||||
[_('Center'), _('Bottom Left')])
|
||||
|
||||
adj_grid.addWidget(self.skew_ref_label, 18, 0)
|
||||
adj_grid.addWidget(self.skew_ref_combo, 18, 1)
|
||||
|
||||
Reference in New Issue
Block a user