def test_viewport_grbl(self): """ Test GRBL-esque viewport. :return: """ bed_size = "225mm" view = ViewPort( bed_size, bed_size, native_scale_x=UNITS_PER_MIL, native_scale_y=UNITS_PER_MIL, origin_x=0, origin_y=1, flip_y=True, ) x, y = view.scene_to_device_position(0, 0) self.assertGreaterEqual(x, -10) self.assertGreaterEqual(10, x) self.assertGreaterEqual(y, Length(bed_size).mil - 10) self.assertGreaterEqual(Length(bed_size).mil + 10, y) x, y = view.scene_to_device_position(0, float(Length(bed_size))) self.assertGreaterEqual(x, -10) self.assertGreaterEqual(10, x) self.assertGreaterEqual(y, -10) self.assertGreaterEqual(10, y) x, y = view.device_to_scene_position(0, Length(bed_size).mil) self.assertGreaterEqual(x, -10) self.assertGreaterEqual(10, x) self.assertGreaterEqual(y, -10) self.assertGreaterEqual(10, y)
def on_button_set_home_current( self, event): # wxGlade: MoshiDriverGui.<event_handler> current_x, current_y = self.context.device.current self.context.home_x = "%.1fmm" % Length(amount=current_x).mm self.context.home_y = "%.1fmm" % Length(amount=current_y).mm self.text_home_x.SetValue(self.context.home_x) self.text_home_y.SetValue(self.context.home_y)
def test_viewport_arbitrary(self): """ Test arbitrary viewport. :return: """ bed_width = Length(amount=random.randint(0, 65535 * 1000)).length_mm bed_height = Length(amount=random.randint(0, 65535 * 1000)).length_mm for i in range(100): view = ViewPort( bed_width, bed_height, user_scale_x=1.0, user_scale_y=1.0, native_scale_x=UNITS_PER_MIL, native_scale_y=UNITS_PER_MIL, origin_x=random.random(), origin_y=random.random(), flip_x=bool(random.randint(0, 1)), flip_y=bool(random.randint(0, 1)), swap_xy=bool(random.randint(0, 1)), ) x, y = view.scene_to_device_position(0, 0) x, y = view.device_to_scene_position(x, y) self.assertGreaterEqual(x, -10) self.assertGreaterEqual(10, x) self.assertGreaterEqual(y, -10) self.assertGreaterEqual(10, y)
def on_btn_create_circle(self, event): # wxGlade: clsLasertools.<event_handler> result, center, radius = self.calculate_center() if result: p = self.context units = p.units_name if self.check_circle.GetValue(): self.context( "circle {x} {y} 1mm stroke black\n".format( x=str( Length(amount=center[0], digits=5, preferred_units=units) ), y=str( Length(amount=center[1], digits=5, preferred_units=units) ), ) ) self.context( "circle {x} {y} {r}\n".format( x=str(Length(amount=center[0], digits=5, preferred_units=units)), y=str(Length(amount=center[1], digits=5, preferred_units=units)), r=str(Length(amount=radius, digits=5, preferred_units=units)), ) ) if self.check_ref_circle.GetValue(): self.context("reference\n") event.Skip()
def set_coord(self, idx=0, position=None): if position is None: label_string = _("<empty>") else: p = self.context units = p.units_name label_string = "({x}, {y})".format( x=str(Length(amount=position[0], digits=1, preferred_units=units)), y=str(Length(amount=position[1], digits=1, preferred_units=units)), ) if idx == 0: self.lbl_pos_1.Label = label_string self.lbl_pos_4.Label = label_string self.lbl_pos_7.Label = label_string self.coord_a = position self.check_input() elif idx == 1: self.lbl_pos_2.Label = label_string self.lbl_pos_5.Label = label_string self.lbl_pos_8.Label = label_string self.coord_b = position self.check_input() elif idx == 2: self.lbl_pos_3.Label = label_string self.lbl_pos_6.Label = label_string self.coord_c = position self.check_input() self.sizer_circle.Layout() self.sizer_square.Layout() self.sizer_rectangle.Layout()
def execute_xy_changes(self, refresh_after = True): if self.position_x == self.org_x and self.position_y == self.org_y: return if self.chk_indivdually.GetValue(): for elem in self.context.elements.flat(types=elem_nodes, emphasized=True): _bb = elem.bounds bb = [_bb[0], _bb[1], _bb[2], _bb[3]] newx = float( Length( "{value}{unit}".format( value=self.position_x, unit=self.position_units ) ) ) newy = float( Length( "{value}{unit}".format( value=self.position_y, unit=self.position_units ) ) ) if self.position_x == self.org_x: dx = 0 else: dx = newx - bb[0] if self.position_y == self.org_y: dy = 0 else: dy = newy - bb[1] # print("Old=%.1f, new=%.1f, dx=%.1f" % (bb[0], newx, dx)) if dx == 0 and dy == 0: continue oldw = bb[2] - bb[0] oldh = bb[3] - bb[1] if dx != 0: bb[0] = newx bb[2] = newx + oldw if dy != 0: bb[1] = newy bb[3] = newy + oldh elem.matrix.post_translate(dx, dy) elem._bounds = bb elem.modified() else: self.context( "resize %f%s %f%s %f%s %f%s\n" % ( self.position_x, self.position_units, self.position_y, self.position_units, self.position_w, self.position_units, self.position_h, self.position_units, ) ) if refresh_after: self.update_position(True)
def execute_wh_changes(self, refresh_after = True): if self.position_w == self.org_w and self.position_h == self.org_h: return if self.chk_indivdually.GetValue(): for elem in self.context.elements.flat(types=elem_nodes, emphasized=True): _bb = elem.bounds bb = [_bb[0], _bb[1], _bb[2], _bb[3]] new_w = float( Length( "{value}{unit}".format( value=self.position_w, unit=self.position_units ) ) ) new_h = float( Length( "{value}{unit}".format( value=self.position_h, unit=self.position_units ) ) ) try: scalex = new_w / (bb[2] - bb[0]) scaley = new_h / (bb[3] - bb[1]) except ZeroDivisionError: continue # print("Old=%.1f, new=%.1f, sx=%.1f" % ((bb[2]-bb[0]), new_w, scalex)) if abs(scalex - 1)<0.000001: scalex = 1 if abs(scaley - 1)<0.000001: scaley = 1 if scalex == 1 and scaley == 1: continue if scalex != 0: bb[2] = bb[0] + (bb[2] - bb[0]) * scalex if scaley != 0: bb[3] = bb[1] + (bb[3] - bb[1]) * scaley elem.matrix.post_scale(scalex, scaley, bb[0], bb[1]) elem._bounds = bb elem.modified() else: cmd = "resize %f%s %f%s %f%s %f%s\n" % ( self.position_x, self.position_units, self.position_y, self.position_units, self.position_w, self.position_units, self.position_h, self.position_units, ) self.context(cmd) if refresh_after: self.update_position(True)
def test_length_parsing(self): self.assertAlmostEqual(Length("10cm"), (Length("100mm"))) self.assertNotEqual(Length("1mm"), 0) self.assertNotEqual(Length("1cm"), 0) self.assertNotEqual(Length("1in"), 0) self.assertNotEqual(Length("1px"), 0) self.assertNotEqual(Length("1pc"), 0) self.assertNotEqual(Length("1pt"), 0) self.assertNotEqual(Length("1%", relative_length=100), 0) self.assertEqual(Length("50%", relative_length=100), 50.0)
def test_viewport_balor_flip_x(self): """ Test Balor-esque viewport. Center x, y. flip_x :return: """ lens_size = "110mm" unit_size = float(Length(lens_size)) galvo_range = 0xFFFF units_per_galvo = unit_size / galvo_range view = ViewPort( lens_size, lens_size, native_scale_x=units_per_galvo, native_scale_y=units_per_galvo, origin_x=0.5, origin_y=0.5, flip_x=True, ) x, y = view.device_to_scene_position(0x8000, 0x8000) self.assertGreaterEqual(x, -10) self.assertGreaterEqual(10, x) self.assertGreaterEqual(y, -10) self.assertGreaterEqual(10, y)
def on_btn_create_frame(self, event): # wxGlade: clsLasertools.<event_handler> result, left_top, width, height = self.calculate_frame() if result: p = self.context units = p.units_name self.context( "rect {x} {y} {wd} {ht}\n".format( x=str(Length(amount=left_top[0], digits=5, preferred_units=units)), y=str(Length(amount=left_top[1], digits=5, preferred_units=units)), wd=str(Length(amount=width, digits=5, preferred_units=units)), ht=str(Length(amount=height, digits=5, preferred_units=units)), ) ) if self.check_ref_frame.GetValue(): self.context("reference\n") event.Skip()
def test_length_addition(self): a = Length("0mm", digits=2) a += "2in" a += "2mil" a *= 1.7 self.assertEqual(str(a), "86.45mm") self.assertEqual(a.mm, 86.45) self.assertEqual(a.um, 86446.36) self.assertEqual(a.cm, 8.64)
def direction(command, channel, _, data=None, amount=None, **kwgs): if data is None: data = kernel.device.spooler spooler = data if amount is None: amount = Length("1mm") if not hasattr(spooler, "_dx"): spooler._dx = Length(0) if not hasattr(spooler, "_dy"): spooler._dy = Length(0) if command.endswith("right"): spooler._dx += amount elif command.endswith("left"): spooler._dx -= amount elif command.endswith("up"): spooler._dy -= amount elif command.endswith("down"): spooler._dy += amount kernel.console(".timer 1 0 spool jog\n") return "spooler", spooler
def on_text_home_y(self, event=None): ctrl = self.text_home_y try: length = Length(ctrl.GetValue()) ctrl.SetBackgroundColour(None) ctrl.Refresh() except ValueError: ctrl.SetBackgroundColour(wx.RED) ctrl.Refresh() return self.context.home_y = length.preferred_length
def on_selstrokewidth(self, origin, stroke_width, *args): # Stroke_width is a text # print("Signal with %s" % stroke_width) sw = float(Length(stroke_width)) for e in self.context.elements.flat(types=elem_nodes, emphasized=True): try: e.stroke_width = sw e.altered() except AttributeError: # Ignore and carry on... continue self.request_refresh()
def test_viewport_lihuiyu_swap_xy(self): """ Test Lihuiyu-esque viewport. User-scaled to mm :return: """ bed_width = "330mm" bed_height = "225mm" view = ViewPort( bed_width, bed_height, user_scale_x=1.2, user_scale_y=1.0, native_scale_x=UNITS_PER_MIL, native_scale_y=UNITS_PER_MIL, origin_x=0, origin_y=0, swap_xy=True, ) x, y = view.scene_to_device_position(0, 0) self.assertGreaterEqual(x, -1) self.assertGreaterEqual(1, x) self.assertGreaterEqual(y, -1) self.assertGreaterEqual(1, y) x, y = view.scene_to_device_position(0, 0) self.assertGreaterEqual(x, -10) self.assertGreaterEqual(10, x) self.assertGreaterEqual(y, -10) self.assertGreaterEqual(10, y) x, y = view.device_to_scene_position(0, Length(bed_height).mil) self.assertGreaterEqual(x, float(Length(bed_height)) - 10) self.assertGreaterEqual(float(Length(bed_height)) + 10, x) self.assertGreaterEqual(y, -10) self.assertGreaterEqual(10, y) x, y = view.device_to_scene_position( Length(bed_width).mil, Length(bed_height).mil) self.assertGreaterEqual(x, float(Length(bed_height)) - 10) self.assertGreaterEqual(float(Length(bed_height)) + 10, x) self.assertGreaterEqual(y, float(Length(bed_width)) * 1.2 - 10) self.assertGreaterEqual(float(Length(bed_width)) * 1.2 + 10, y)
def jog(command, channel, _, data, force=False, **kwgs): if data is None: data = kernel.device.spooler spooler = data try: idx = spooler._dx idy = spooler._dy except AttributeError: return if force: spooler.command("move_rel", idx, idy) spooler._dx = Length(0) spooler._dy = Length(0) else: if spooler.is_idle: spooler.command("move_rel", float(idx), float(idy)) channel(_("Position moved: {x} {y}").format(x=idx, y=idy)) spooler._dx = Length(0) spooler._dy = Length(0) else: channel(_("Busy Error")) return "spooler", spooler
def eulerian_fill(settings, outlines, matrix, limit=None): """ Applies optimized Eulerian fill @return: """ if matrix is None: matrix = Matrix() settings = dict(settings) h_dist = settings.get("hatch_distance", "1mm") h_angle = settings.get("hatch_angle", "0deg") distance_y = float(Length(h_dist)) if isinstance(h_angle, float): angle = Angle.degrees(h_angle) else: angle = Angle.parse(h_angle) rotate = Matrix.rotate(angle) counter_rotate = Matrix.rotate(-angle) def mx_rotate(pt): if pt is None: return None return ( pt[0] * rotate.a + pt[1] * rotate.c + 1 * rotate.e, pt[0] * rotate.b + pt[1] * rotate.d + 1 * rotate.f, ) def mx_counter(pt): if pt is None: return None return ( pt[0] * counter_rotate.a + pt[1] * counter_rotate.c + 1 * counter_rotate.e, pt[0] * counter_rotate.b + pt[1] * counter_rotate.d + 1 * counter_rotate.f, ) transformed_vector = matrix.transform_vector([0, distance_y]) distance = abs(complex(transformed_vector[0], transformed_vector[1])) efill = EulerianFill(distance) for sp in outlines: sp = list(map(mx_rotate, sp)) efill += sp if limit and efill.estimate() > limit: return [] points = efill.get_fill() points = list(map(mx_counter, points)) return points
def text(event=None): try: v = Length(ctrl.GetValue()) ctrl.SetBackgroundColour(None) ctrl.Refresh() except ValueError: ctrl.SetBackgroundColour(wx.RED) ctrl.Refresh() return try: data_v = v.preferred_length setattr(obj, param, data_v) self.context.signal(param, data_v) except ValueError: # cannot cast to data_type, pass pass
def on_text_bedheight(self, event=None): ctrl = self.text_bedheight try: bedheight = Length(ctrl.GetValue()) ctrl.SetBackgroundColour(None) ctrl.Refresh() except ValueError: ctrl.SetBackgroundColour(wx.RED) ctrl.Refresh() return self.context.device.height = bedheight.preferred_length self.context.device.bedheight = bedheight.preferred_length self.context.signal( "bed_size", (self.context.device.bedwidth, self.context.device.bedheight)) self.context.device.realize() self.context("viewport_update\n")
def on_btn_create_square(self, event): # wxGlade: clsLasertools.<event_handler> try: dim_x = Length(self.txt_width.GetValue()) dim_y = Length(self.txt_width.GetValue()) except ValueError: dim_x = Length(DEFAULT_LEN) dim_y = Length(DEFAULT_LEN) result, center, angle, signx, signy = self.calculate_square() dim_x *= signx dim_y *= signy angle = angle * 360 / tau if result: p = self.context units = p.units_name # self.context("circle {x}mm {y}mm 2mm stroke green".format(x=round(self.coord_a[0]/UNITS_PER_MM,2),y=round(self.coord_a[1]/UNITS_PER_MM,2)) ) # self.context("circle {x}mm {y}mm 2mm stroke green".format(x=round(self.coord_b[0]/UNITS_PER_MM,2),y=round(self.coord_b[1]/UNITS_PER_MM,2)) ) # self.context("circle {x}mm {y}mm 2mm stroke red".format(x=round(self.coord_c[0]/UNITS_PER_MM,2),y=round(self.coord_c[1]/UNITS_PER_MM,2)) ) if self.check_square.GetValue(): self.context( "circle {x} {y} 1mm stroke black\n".format( x=str( Length(amount=center[0], digits=5, preferred_units=units) ), y=str( Length(amount=center[1], digits=5, preferred_units=units) ), ) ) self.context( "rect {x} {y} {wd} {ht} rotate {angle}deg -x {x} -y {y}\n".format( x=str(Length(amount=center[0], digits=5, preferred_units=units)), y=str(Length(amount=center[1], digits=5, preferred_units=units)), wd=str(dim_x.length_mm), ht=str(dim_y.length_mm), angle=angle, ) ) if self.check_ref_square.GetValue(): self.context("reference\n") event.Skip()
def preprocess(self, context, matrix, commands): """ Process the scale to native resolution done with the given matrix. In the case of image ops we are scaling the overscan length into usable native units. @param matrix: @return: """ overscan = float(Length(self.settings.get("overscan", "1mm"))) transformed_vector = matrix.transform_vector([0, overscan]) self.overscan = abs(complex(transformed_vector[0], transformed_vector[1])) for node in self.children: def actual(image_node): def process_images(): image_node._context = context image_node.process_image() return process_images commands.append(actual(node))
def fill_magnets(self): # Let's set the full grid p = self.scene.context if self.scene.draw_grid_primary: tlen = float( Length("{value}{units}".format(value=self.scene.tick_distance, units=p.units_name))) amount = (round( (p.device.unit_width / tlen) * (p.device.unit_height / tlen) / 1000, 0, ) * 1000) if amount >= 2000: dlg = wx.MessageDialog( None, _("You will create more than {:,.0f} magnet-lines! Are you really, really sure?" ).format(amount), _("Huge amount of magnet lines"), wx.YES_NO | wx.ICON_QUESTION, ) result = dlg.ShowModal() dlg.Destroy() if result == wx.ID_NO: return x = 0 while x <= p.device.unit_width: self.scene.toggle_x_magnet(x) x += tlen y = 0 while y <= p.device.unit_height: self.scene.toggle_y_magnet(y) y += tlen elif self.scene.draw_grid_secondary: # Placeholder for a use case, as you can define them manually... pass
def test_viewport_grbl_user_scale(self): """ Test GRBL-esque viewport. User-scaled to mm :return: """ bed_size = "225mm" view = ViewPort( bed_size, bed_size, user_scale_x=1.0 / Length("1mil").mm, user_scale_y=1.0 / Length("1mil").mm, native_scale_x=UNITS_PER_MIL, native_scale_y=UNITS_PER_MIL, origin_x=0, origin_y=1, flip_y=True, ) x, y = view.scene_to_device_position(0, 0) self.assertGreaterEqual(x, -1) self.assertGreaterEqual(1, x) self.assertGreaterEqual(y, Length(bed_size).mm - 1) self.assertGreaterEqual(Length(bed_size).mm + 1, y) x, y = view.scene_to_device_position(0, float(Length(bed_size))) self.assertGreaterEqual(x, -10) self.assertGreaterEqual(10, x) self.assertGreaterEqual(y, -10) self.assertGreaterEqual(10, y) x, y = view.device_to_scene_position(0, Length(bed_size).mm) self.assertGreaterEqual(x, -10) self.assertGreaterEqual(10, x) self.assertGreaterEqual(y, -10) self.assertGreaterEqual(10, y)
def preprocess(self, context, matrix, commands): """ Preprocess is called during job planning. This should be called with the native matrix. @param context: @param matrix: @param commands: @return: """ overscan = float(Length(self.settings.get("overscan", "1mm"))) transformed_vector = matrix.transform_vector([0, overscan]) self.overscan = abs( complex(transformed_vector[0], transformed_vector[1])) # Calculate raster steps from DPI device context step_x, step_y = context.device.dpi_to_steps(self.dpi, matrix=matrix) self.raster_step_x, self.raster_step_y = step_x, step_y if len(self.children) == 0: return if len(self.children) == 1 and self.children[0].type == "elem image": node = self.children[0] node.step_x = step_x node.step_y = step_y m = node.matrix # Transformation must be uniform to permit native rastering. if m.a != step_x or m.b != 0.0 or m.c != 0.0 or m.d != step_y: def actualize_raster_image(image_node, s_x, s_y): def actualize_raster_image_node(): image_node.image, image_node.matrix = actualize( image_node.image, image_node.matrix, step_x=s_x, step_y=s_y) image_node.cache = None return actualize_raster_image_node commands.append(actualize_raster_image(node, step_x, step_y)) return make_raster = context.lookup("render-op/make_raster") if make_raster is None: def strip_rasters(): self.remove_node() commands.append(strip_rasters) return def make_image(): step_x = self.raster_step_x step_y = self.raster_step_y bounds = self.bounds try: image = make_raster(list(self.flat()), bounds=bounds, step_x=step_x, step_y=step_y) except AssertionError: raise CutPlanningFailedError("Raster too large.") if image.width == 1 and image.height == 1: # TODO: Solve this is a less kludgy manner. The call to make the image can fail the first time # around because the renderer is what sets the size of the text. If the size hasn't already # been set, the initial bounds are wrong. bounds = self.bounds try: image = make_raster(list(self.flat()), bounds=bounds, step_x=step_x, step_y=step_y) except AssertionError: raise CutPlanningFailedError("Raster too large.") image = image.convert("L") matrix = Matrix.scale(step_x, step_y) matrix.post_translate(bounds[0], bounds[1]) image_node = ImageNode(image=image, matrix=matrix) self.children.clear() self.add_node(image_node) commands.append(make_image)
def on_button_set_home_current(self, event=None): current_x, current_y = self.context.device.current self.context.home_x = "%.1fmm" % Length(amount=current_x).mm self.context.home_y = "%.1fmm" % Length(amount=current_y).mm self.text_home_x.SetValue(self.context.home_x) self.text_home_y.SetValue(self.context.home_y)
def scanline_fill(settings, outlines, matrix, limit=None): """ Applies optimized scanline fill @return: """ if matrix is None: matrix = Matrix() settings = dict(settings) h_dist = settings.get("hatch_distance", "1mm") h_angle = settings.get("hatch_angle", "0deg") distance_y = float(Length(h_dist)) if isinstance(h_angle, float): angle = Angle.degrees(h_angle) else: angle = Angle.parse(h_angle) rotate = Matrix.rotate(angle) counter_rotate = Matrix.rotate(-angle) def mx_rotate(pt): if pt is None: return None return ( pt[0] * rotate.a + pt[1] * rotate.c + 1 * rotate.e, pt[0] * rotate.b + pt[1] * rotate.d + 1 * rotate.f, ) def mx_counter(pt): if pt is None: return None return ( pt[0] * counter_rotate.a + pt[1] * counter_rotate.c + 1 * counter_rotate.e, pt[0] * counter_rotate.b + pt[1] * counter_rotate.d + 1 * counter_rotate.f, ) transformed_vector = matrix.transform_vector([0, distance_y]) distance = abs(complex(transformed_vector[0], transformed_vector[1])) vm = VectorMontonizer() for outline in outlines: pts = list(map(Point, map(mx_rotate, outline))) vm.add_cluster(pts) vm.sort_clusters() y_max = vm.clusters[-1][0] y_min = vm.clusters[0][0] height = y_max - y_min try: count = height / distance except ZeroDivisionError: return [] if limit and count > limit: return [] vm.valid_low_value = y_min - distance vm.valid_high_value = y_max + distance vm.scanline(y_min - distance) points = list() forward = True while vm.valid_range(): vm.next_intercept(distance) vm.sort_actives() y = vm.current for i in ( range(1, len(vm.actives), 2) if forward else range(len(vm.actives) - 1, 0, -2) ): left_segment = vm.actives[i - 1] right_segment = vm.actives[i] left_segment_x = vm.intercept(left_segment, y) right_segment_x = vm.intercept(right_segment, y) if forward: points.append((left_segment_x, y)) points.append((right_segment_x, y)) else: points.append((right_segment_x, y)) points.append((left_segment_x, y)) points.append(None) forward = not forward points = list(map(mx_counter, points)) return points
def show_grid( command, channel, _, target=None, ox=None, oy=None, scalex=None, scaley=None, **kwargs, ): if target is None: channel(_("Grid-Parameters:")) channel( "Primary: %s" % _("On") if self.widget_scene.draw_grid_primary else _("Off") ) if self.widget_scene.draw_grid_secondary: channel("Secondary: %s" % _("On")) if not self.widget_scene.grid_secondary_cx is None: channel( " cx: %s" % Length( amount=self.widget_scene.grid_secondary_cx ).length_mm ) if not self.widget_scene.grid_secondary_cy is None: channel( " cy: %s" % Length( amount=self.widget_scene.grid_secondary_cy ).length_mm ) if not self.widget_scene.grid_secondary_scale_x is None: channel( " scale-x: %.2f" % self.widget_scene.grid_secondary_scale_x ) if not self.widget_scene.grid_secondary_scale_y is None: channel( " scale-y: %.2f" % self.widget_scene.grid_secondary_scale_y ) else: channel("Secondary: %s" % _("Off")) if self.widget_scene.draw_grid_circular: channel("Circular: %s" % _("On")) if not self.widget_scene.grid_circular_cx is None: channel( " cx: %s" % Length( amount=self.widget_scene.grid_circular_cx ).length_mm ) if not self.widget_scene.grid_circular_cy is None: channel( " cy: %s" % Length( amount=self.widget_scene.grid_circular_cy ).length_mm ) else: channel("Circular: %s" % _("Off")) return else: target = target.lower() if target[0] == "p": self.widget_scene.draw_grid_primary = ( not self.widget_scene.draw_grid_primary ) channel( _( "Turned primary grid on" if self.widget_scene.draw_grid_primary else "Turned primary grid off" ) ) self.scene.signal("guide") self.scene.signal("grid") self.request_refresh() elif target[0] == "s": self.widget_scene.draw_grid_secondary = ( not self.widget_scene.draw_grid_secondary ) if self.widget_scene.draw_grid_secondary: if ox is None: self.widget_scene.grid_secondary_cx = None self.widget_scene.grid_secondary_cy = None scalex = None scaley = None else: if oy is None: oy = ox self.widget_scene.grid_secondary_cx = float( Length(ox, relative_length=self.context.device.width) ) self.widget_scene.grid_secondary_cy = float( Length(oy, relative_length=self.context.device.height) ) if scalex is None: rot = self.scene.context.rotary if rot.rotary_enabled: scalex = rot.scale_x scaley = rot.scale_y else: scalex = 1.0 scaley = 1.0 else: scalex = float(scalex) if scaley is None: scaley = scalex else: scaley = float(scaley) self.widget_scene.grid_secondary_scale_x = scalex self.widget_scene.grid_secondary_scale_y = scaley channel( _( "Turned secondary grid on" if self.widget_scene.draw_grid_secondary else "Turned secondary grid off" ) ) self.scene.signal("guide") self.scene.signal("grid") self.request_refresh() elif target[0] == "c": self.widget_scene.draw_grid_circular = ( not self.widget_scene.draw_grid_circular ) if self.widget_scene.draw_grid_circular: if ox is None: self.widget_scene.grid_circular_cx = None self.widget_scene.grid_circular_cy = None else: if oy is None: oy = ox self.widget_scene.grid_circular_cx = float( Length(ox, relative_length=self.context.device.width) ) self.widget_scene.grid_circular_cy = float( Length(oy, relative_length=self.context.device.height) ) channel( _( "Turned circular grid on" if self.widget_scene.draw_grid_circular else "Turned circular grid off" ) ) self.scene.signal("guide") self.scene.signal("grid") self.request_refresh() else: channel(_("Target needs to be one of primary, secondary, circular"))
def calculate_gridsize(self, w, h): scaled_conversion = ( self.scene.context.device.length( str(1) + self.scene.context.units_name, as_float=True) * self.scene.widget_root.scene_widget.matrix.value_scale_x()) points = self.scene.tick_distance * scaled_conversion p = self.scene.context self.sx = p.device.unit_width * p.device.show_origin_x self.sy = p.device.unit_height * p.device.show_origin_y if self.scene.grid_secondary_cx is None: self.sx2 = self.sx else: self.sx2 = self.scene.grid_secondary_cx if self.scene.grid_secondary_cy is None: self.sy2 = self.sy else: self.sy2 = self.scene.grid_secondary_cy if self.scene.grid_circular_cx is None: self.cx = self.sx else: self.cx = self.scene.grid_circular_cx if self.scene.grid_circular_cy is None: self.cy = self.sy else: self.cy = self.scene.grid_circular_cy self.min_x, self.min_y = self.scene.convert_window_to_scene([0, 0]) self.min_x = max(0, self.min_x) self.min_y = max(0, self.min_y) self.max_x, self.max_y = self.scene.convert_window_to_scene([w, h]) self.max_x = min(float(self.scene.context.device.unit_width), self.max_x) self.max_y = min(float(self.scene.context.device.unit_height), self.max_y) tlen = float( Length("{value}{units}".format( value=self.scene.tick_distance, units=self.scene.context.units_name))) if tlen == 0: tlen = float(Length("10mm")) self.tlenx1 = tlen self.tleny1 = tlen self.sxx1 = round(self.min_x / self.tlenx1, 0) * (self.tlenx1 + 1) while self.sxx1 > self.min_x: self.sxx1 -= self.tlenx1 if self.sxx1 < self.min_x: self.sxx1 += self.tlenx1 self.syy1 = round(self.min_y / self.tleny1, 0) * (self.tleny1 + 1) while self.syy1 > self.min_y: self.syy1 -= self.tleny1 if self.syy1 < self.min_y: self.syy1 += self.tleny1 self.tlenx2 = tlen * self.scene.grid_secondary_scale_x self.tleny2 = tlen * self.scene.grid_secondary_scale_y self.sxx2 = round(self.min_x / self.tlenx2, 0) * (self.tlenx2 + 1) while self.sxx2 > self.min_x: self.sxx2 -= self.tlenx2 if self.sxx2 < self.min_x: self.sxx2 += self.tlenx2 self.syy2 = round(self.min_y / self.tleny2, 0) * (self.tleny2 + 1) while self.syy2 > self.min_y: self.syy2 -= self.tleny2 if self.syy2 < self.min_y: self.syy2 += self.tleny2 # lets establish which circles we really have to draw self.min_radius = float("inf") self.max_radius = -float("inf") test_points = ( # all 4 corners (self.min_x, self.min_y), (self.min_x, self.max_y), (self.max_x, self.min_y), (self.max_x, self.max_y), # and the boundary points aligned with the center (self.cx, self.max_y), (self.cx, self.min_y), (self.min_x, self.cy), (self.max_x, self.cy), ) for i, pt in enumerate(test_points): dx = pt[0] - self.cx dy = pt[1] - self.cy r = sqrt(dx * dx + dy * dy) if r < self.min_radius: self.min_radius = r if r > self.max_radius: self.max_radius = r # 1 | 2 | 3 # --+---+-- # 4 | 5 | 6 # --+---+-- # 7 | 8 | 9 min_a = float("inf") max_a = -float("inf") if self.cx <= self.min_x: # left if self.cy <= self.min_y: # below quadrant = 7 pt1 = (self.min_x, self.max_y) pt2 = (self.max_x, self.min_y) elif self.cy >= self.max_y: # above quadrant = 1 pt1 = (self.max_x, self.max_y) pt2 = (self.min_x, self.min_y) else: # between quadrant = 4 pt1 = (self.min_x, self.max_y) pt2 = (self.min_x, self.min_y) elif self.cx >= self.max_x: # right if self.cy <= self.min_y: # below quadrant = 9 pt1 = (self.min_x, self.min_y) pt2 = (self.max_x, self.max_y) elif self.cy >= self.max_y: # above quadrant = 3 pt1 = (self.max_x, self.min_y) pt2 = (self.min_x, self.max_y) else: # between quadrant = 6 pt1 = (self.max_x, self.min_y) pt2 = (self.max_x, self.max_y) else: # between if self.cy <= self.min_y: # below quadrant = 8 pt1 = (self.min_x, self.min_y) pt2 = (self.max_x, self.min_y) elif self.cy >= self.max_y: # above quadrant = 2 pt1 = (self.max_x, self.max_y) pt2 = (self.min_x, self.max_y) else: # between quadrant = 5 pt1 = None pt2 = None min_a = 0 max_a = tau self.sector = quadrant if not pt1 is None: dx1 = pt1[0] - self.cx dy1 = pt1[1] - self.cy dx2 = pt2[0] - self.cx dy2 = pt2[1] - self.cy max_a = self.calc_atan(dx1, dy1) min_a = self.calc_atan(dx2, dy2) while max_a < min_a: max_a += tau while min_a < 0: min_a += tau max_a += tau self.min_angle = min_a self.max_angle = max_a if self.min_x < self.cx < self.max_x and self.min_y < self.cy < self.max_y: self.min_radius = 0
def as_cutobjects(self, closed_distance=15, passes=1): """ Generator of cutobjects for a raster operation. This takes any image node children and converts them into rastercut objects. These objects should have already been converted from vector shapes. The preference for raster shapes is to use the settings set on this operation rather than on the image-node. """ settings = self.derive() # Set overscan overscan = self.overscan if not isinstance(overscan, float): overscan = float(Length(overscan)) settings["overscan"] = overscan # Set steps step_x = self.raster_step_x step_y = self.raster_step_y assert step_x != 0 assert step_y != 0 settings["raster_step_x"] = step_x settings["raster_step_x"] = step_y # Set variables by direction direction = self.raster_direction horizontal = False start_on_left = False start_on_top = False if direction == 0 or direction == 4: horizontal = True start_on_top = True elif direction == 1: horizontal = True start_on_top = False elif direction == 2: horizontal = False start_on_left = False elif direction == 3: horizontal = False start_on_left = True bidirectional = bool(self.raster_swing) for image_node in self.children: # Process each child. Some core settings are the same for each child. if image_node.type != "elem image": continue # Perform correct actualization image_node.step_x = step_x image_node.step_y = step_y image_node.process_image() # Set variables matrix = image_node.matrix pil_image = image_node.image offset_x = matrix.value_trans_x() offset_y = matrix.value_trans_y() # Establish path min_x = offset_x min_y = offset_y max_x = offset_x + pil_image.width * step_x max_y = offset_y + pil_image.height * step_y path = Path( Polygon( (min_x, min_y), (min_x, max_y), (max_x, max_y), (max_x, min_y), )) # Create Cut Object cut = RasterCut( image=pil_image, offset_x=offset_x, offset_y=offset_y, step_x=step_x, step_y=step_y, inverted=False, bidirectional=bidirectional, horizontal=horizontal, start_on_top=start_on_top, start_on_left=start_on_left, overscan=overscan, settings=settings, passes=passes, ) cut.path = path cut.original_op = self.type yield cut if direction == 4: # Create optional crosshatch cut horizontal = False start_on_left = False cut = RasterCut( image=pil_image, offset_x=offset_x, offset_y=offset_y, step_x=step_x, step_y=step_y, inverted=False, bidirectional=bidirectional, horizontal=horizontal, start_on_top=start_on_top, start_on_left=start_on_left, overscan=overscan, settings=settings, passes=passes, ) cut.path = path cut.original_op = self.type yield cut
def update_position(self, reset): more_than_one = False ct = 0 for e in self.context.elements.flat(types=elem_nodes, emphasized=True): ct += 1 if ct > 1: more_than_one = True break bounds = self.context.elements.selected_area() if bounds is None: if self.text_x.IsEnabled(): self.text_w.Enable(False) self.text_h.Enable(False) self.text_x.Enable(False) self.text_y.Enable(False) self.button_execute.Enable(False) self.chk_indivdually.SetValue(False) self.chk_indivdually.Enable(False) self.chk_lock.Enable(False) self.combo_box_units.Enable(False) if self.position_units in self.choices: self.combo_box_units.SetSelection( self.choices.index(self.position_units) ) return if not self.text_x.IsEnabled(): self.text_w.Enable(True) self.text_h.Enable(True) self.text_x.Enable(True) self.text_y.Enable(True) self.combo_box_units.Enable(True) self.button_execute.Enable(True) self.chk_indivdually.SetValue(False) self.chk_lock.Enable(True) self.chk_indivdually.Enable(more_than_one) if reset: x0, y0, x1, y1 = bounds # conversion = ViewPort.conversion(self.position_units) conversion = float( Length("{amount}{units}".format(units=self.position_units, amount=1)) ) # print ("Size: x0 = %.2f, conversion=%.5f, new=%.2f (units %s)" % (x0, conversion, x0/conversion, self.position_units)) self.position_x = x0 / conversion self.position_y = y0 / conversion self.position_w = (x1 - x0) / conversion self.position_h = (y1 - y0) / conversion self.org_x = self.position_x self.org_y = self.position_y self.org_w = self.position_w self.org_h = self.position_h if self.position_units == "%": self.text_x.SetValue("%.2f" % 100) self.text_y.SetValue("%.2f" % 100) self.text_w.SetValue("%.2f" % 100) self.text_h.SetValue("%.2f" % 100) else: self.text_x.SetValue("%.2f" % self.position_x) self.text_y.SetValue("%.2f" % self.position_y) self.text_w.SetValue("%.2f" % self.position_w) self.text_h.SetValue("%.2f" % self.position_h) self.combo_box_units.SetSelection(self.choices.index(self.position_units))