Example #1
0
    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)
Example #2
0
 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)
Example #3
0
    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)
Example #4
0
 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()
Example #5
0
 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()
Example #6
0
 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)
Example #7
0
    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)
Example #8
0
 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)
Example #9
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)
Example #10
0
    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()
Example #11
0
    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)
Example #12
0
 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
Example #13
0
 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
Example #14
0
 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()
Example #15
0
    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)
Example #16
0
 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
Example #17
0
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
Example #18
0
 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
Example #19
0
 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")
Example #20
0
    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()
Example #21
0
    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))
Example #22
0
    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
Example #23
0
    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)
Example #24
0
    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)
Example #25
0
 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)
Example #26
0
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
Example #27
0
        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"))
Example #28
0
    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
Example #29
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
Example #30
0
    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))