Beispiel #1
0
 def test_numpath_transform(self):
     numpath = Numpath()
     numpath.polyline(
         (
             complex(0.05, 0.05),
             complex(0.95, 0.05),
             complex(0.95, 0.95),
             complex(0.05, 0.95),
             complex(0.05, 0.05),
         )
     )
     numpath.polyline(
         (
             complex(0.25, 0.25),
             complex(0.75, 0.25),
             complex(0.75, 0.75),
             complex(0.25, 0.75),
             complex(0.25, 0.25),
         )
     )
     numpath.uscale(10000)
     c = copy(numpath)
     numpath.rotate(tau * 0.25)
     c.transform(Matrix("rotate(.25turn)"))
     t = numpath.segments == c.segments
     self.assertTrue(np.all(t))
Beispiel #2
0
 def validate(self):
     if self.point is None:
         self.point = Point(
             float(self.settings.get("x", 0)), float(self.settings.get("y", 0))
         )
     if self.matrix is None:
         self.matrix = Matrix()
Beispiel #3
0
 def __init__(
     self,
     path=None,
     matrix=None,
     fill=None,
     stroke=None,
     stroke_width=None,
     linecap=Linecap.CAP_BUTT,
     linejoin=Linejoin.JOIN_MITER,
     fillrule=Fillrule.FILLRULE_NONZERO,
     *args,
     **kwargs,
 ):
     super().__init__(*args, **kwargs)
     self._formatter = "{element_type} {id} {stroke}"
     self.settings.update(kwargs)
     self.path = path
     if matrix is None:
         matrix = Matrix()
     self.matrix = matrix
     self.fill = fill
     self.stroke = stroke
     self.stroke_width = stroke_width
     self.linecap = linecap
     self.linejoin = linejoin
     self.fillrule = fillrule
     self.lock = False
Beispiel #4
0
    def test_fill_hatch2(self):
        kernel = bootstrap.bootstrap()
        try:
            kernel.console("operation* delete\n")
            kernel.console("rect 0 0 1in 1in\n")
            kernel.console("rect 3in 0 1in 1in\n")
            kernel.console("hatch\n")
            hatch = list(kernel.elements.ops())[0]
            hatch.hatch_type = "eulerian"
            rect0 = list(kernel.elements.elems())[0]
            hatch.add_node(copy(rect0))
            rect1 = list(kernel.elements.elems())[1]
            hatch.add_node(copy(rect1))
            commands = list()
            # kernel.console("tree list\n")
            hatch.preprocess(kernel.root, Matrix(), commands)
            for command in commands:
                command()
            # kernel.console("tree list\n")
            polyline_node0 = hatch.children[0]
            shape0 = polyline_node0.shape
            self.assertEqual(len(shape0), 77)
            # print(shape0)

            polyline_node1 = hatch.children[1]
            shape1 = polyline_node1.shape
            self.assertEqual(len(shape1), 50)
            # print(shape1)
        finally:
            kernel.shutdown()
Beispiel #5
0
    def rebuild_hit_chain(self, current_widget, current_matrix=None):
        """
        Iterates through the hit chain to find elements which respond to their hit() function that they are HITCHAIN_HIT
        and registers this within the hittable_elements list if they are able to hit at the current time. Given the
        dimensions of the widget and the current matrix within the widget tree.

        HITCHAIN_HIT means that this is a hit value and should the termination of this branch of the widget tree.
        HITCHAIN_DELEGATE means that this is not a hittable widget and should not receive mouse events.
        HITCHAIN_HIT_AND_DELEGATE means that this is a hittable widget, but other widgets within it might also matter.
        HITCHAIN_DELEGATE_AND_HIT means that other widgets in the tree should be checked first, but after those this
        widget should be checked.

        The hitchain is the current matrix and current widget in the order of depth.

        """
        # If there is a matrix for the widget concatenate it.
        if current_widget.matrix is not None:
            matrix_within_scene = Matrix(current_widget.matrix)
            matrix_within_scene.post_cat(current_matrix)
        else:
            matrix_within_scene = Matrix(current_matrix)

        # Add to list and recurse for children based on response.
        response = current_widget.hit()
        if response == HITCHAIN_HIT:
            self.hittable_elements.append(
                (current_widget, matrix_within_scene))
        # elif response == HITCHAIN_HIT_WITH_PRIORITY:
        #    self.hittable_elements.insert(0, (current_widget, matrix_within_scene))
        elif response == HITCHAIN_DELEGATE:
            for w in current_widget:
                self.rebuild_hit_chain(w, matrix_within_scene)
        elif response == HITCHAIN_HIT_AND_DELEGATE:
            self.hittable_elements.append(
                (current_widget, matrix_within_scene))
            for w in current_widget:
                self.rebuild_hit_chain(w, matrix_within_scene)
        elif response == HITCHAIN_DELEGATE_AND_HIT:
            for w in current_widget:
                self.rebuild_hit_chain(w, matrix_within_scene)
            self.hittable_elements.append(
                (current_widget, matrix_within_scene))
    def test_actualize_circle_step3_direct_black(self):
        """
        Test for edge pixel error. Black Empty.

        :return:
        """
        image = Image.new("RGBA", (256, 256), "black")
        draw = ImageDraw.Draw(image)
        draw.ellipse((100, 100, 150, 150), "white")

        for step in range(1, 20):
            transform = Matrix()
            actual, transform = actualize(
                image, transform, step_x=step, step_y=step, crop=False, inverted=True
            )
            self.assertEqual(actual.getpixel((-1, -1)), 0)

        # Note: inverted flag not set. White edge pixel is correct.
        actual, transform = actualize(image, Matrix(), step_x=3, step_y=3, crop=False)
        self.assertEqual(actual.getpixel((-1, -1)), 255)
Beispiel #7
0
    def test_plotplanner_walk_raster(self):
        """
        Test plotplanner operation of walking to a raster.

        PLOT_FINISH = 256
        PLOT_RAPID = 4
        PLOT_JOG = 2
        PLOT_SETTING = 128
        PLOT_AXIS = 64
        PLOT_DIRECTION = 32
        PLOT_LEFT_UPPER = 512
        PLOT_RIGHT_LOWER = 1024

        1 means cut.
        0 means move.

        :return:
        """

        rasterop = RasterOpNode()
        image = Image.new("RGBA", (256, 256))
        draw = ImageDraw.Draw(image)
        draw.ellipse((0, 0, 255, 255), "black")
        image = image.convert("L")
        inode = ImageNode(image=image, dpi=1000.0, matrix=Matrix())
        inode.step_x = 1
        inode.step_y = 1
        inode.process_image()
        rasterop.add_node(inode)
        rasterop.raster_step_x = 1
        rasterop.raster_step_y = 1

        vectorop = EngraveOpNode()
        vectorop.add_node(
            PathNode(path=Path(Circle(cx=127, cy=127, r=128)), fill="black"))
        cutcode = CutCode()
        cutcode.extend(vectorop.as_cutobjects())
        cutcode.extend(rasterop.as_cutobjects())
        settings = {"power": 500}
        plan = PlotPlanner(settings)
        for c in cutcode.flat():
            plan.push(c)

        setting_changed = False
        for x, y, on in plan.gen():
            if on > 2:
                if setting_changed:
                    # Settings change happens at vector to raster switch and must repost the axis.
                    self.assertEqual(on, PLOT_AXIS)
                if on == PLOT_SETTING:
                    setting_changed = True
                else:
                    setting_changed = False
Beispiel #8
0
 def apply_rotary_scale(*args, **kwargs):
     sx = self.scale_x
     sy = self.scale_y
     x, y = self.device.current
     matrix = Matrix("scale(%f, %f, %f, %f)" % (sx, sy, x, y))
     for node in self.elements.elems():
         if hasattr(node, "rotary_scale"):
             # This element is already scaled
             return
         try:
             node.rotary_scale = sx, sy
             node.matrix *= matrix
             node.modified()
         except AttributeError:
             pass
Beispiel #9
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
    def test_actualize_circle_step3_direct_white(self):
        """
        Test for edge pixel error. White empty.

        :return:
        """
        image = Image.new("RGBA", (256, 256), "white")
        draw = ImageDraw.Draw(image)
        draw.ellipse((100, 100, 150, 150), "black")

        for step in range(1, 20):
            transform = Matrix()
            actual, transform = actualize(
                image, transform, step_x=step, step_y=step, crop=False
            )
            self.assertEqual(actual.getpixel((-1, -1)), 255)
Beispiel #11
0
 def vectrace(data, **kwargs):
     elements = kernel.root.elements
     path = Path(fill="black", stroke="blue")
     paths = []
     for node in data:
         matrix = node.matrix
         image = node.image
         width, height = node.image.size
         if image.mode != "L":
             image = image.convert("L")
         image = image.point(lambda e: int(e > 127) * 255)
         for points in _vectrace(image.load(), width, height):
             path += Polygon(*points)
         paths.append(
             elements.elem_branch.add(path=path,
                                      matrix=Matrix(matrix),
                                      type="elem path"))
     return "elements", paths
Beispiel #12
0
 def __init__(
     self,
     scene,
     left: float = None,
     top: float = None,
     right: float = None,
     bottom: float = None,
     all: bool = False,
 ):
     """
     All produces a widget of infinite space rather than finite space.
     """
     assert scene.__class__.__name__ == "Scene"
     list.__init__(self)
     self.matrix = Matrix()
     self.scene = scene
     self.parent = None
     self.properties = ORIENTATION_RELATIVE
     if all:
         # contains all points
         self.left = -float("inf")
         self.top = -float("inf")
         self.right = float("inf")
         self.bottom = float("inf")
     else:
         # contains no points
         self.left = float("inf")
         self.top = float("inf")
         self.right = -float("inf")
         self.bottom = -float("inf")
     if left is not None:
         self.left = left
     if right is not None:
         self.right = right
     if top is not None:
         self.top = top
     if bottom is not None:
         self.bottom = bottom
    def test_actualize_pureblack(self):
        """
        Test that a pure black image does not crash.

        :return:
        """
        kernel = bootstrap.bootstrap()
        try:
            kernel_root = kernel.get_context("/")
            # kernel_root("channel print console\n")
            image = Image.new("RGBA", (256, 256), "black")
            elements = kernel_root.elements
            node = elements.elem_branch.add(
                image=image, dpi=1000.0, matrix=Matrix(), type="elem image"
            )
            node.emphasized = True
            kernel_root("image resample\n")
            for element in kernel_root.elements.elems():
                if node.type == "elem image":
                    self.assertEqual(element.image.size, (256, 256))
                    self.assertEqual(element.matrix.value_trans_x(), 0)
                    self.assertEqual(element.matrix.value_trans_y(), 0)
        finally:
            kernel.shutdown()
Beispiel #14
0
 def event(self,
           window_pos=None,
           space_pos=None,
           event_type=None,
           nearest_snap=None):
     response = RESPONSE_CHAIN
     if event_type == "leftclick":
         if nearest_snap is None:
             point = Point(space_pos[0], space_pos[1])
         else:
             point = Point(nearest_snap[0], nearest_snap[1])
         elements = self.scene.context.elements
         node = elements.elem_branch.add(point=point,
                                         matrix=Matrix(),
                                         type="elem point")
         if self.scene.context.elements.default_stroke is not None:
             node.stroke = self.scene.context.elements.default_stroke
         if self.scene.context.elements.default_fill is not None:
             node.fill = self.scene.context.elements.default_fill
         if elements.classify_new:
             elements.classify([node])
         self.notify_created(node)
         response = RESPONSE_CONSUME
     return response
Beispiel #15
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
Beispiel #16
0
    def make_raster(
        self,
        nodes,
        bounds,
        width=None,
        height=None,
        bitmap=False,
        step_x=1,
        step_y=1,
        keep_ratio=False,
        recursion=0,
    ):
        """
        Make Raster turns an iterable of elements and a bounds into an image of the designated size, taking into account
        the step size. The physical pixels in the image is reduced by the step size then the matrix for the element is
        scaled up by the same amount. This makes step size work like inverse dpi and correctly sets the image scale to
        the step scale for 1:1 sizes independent of the scale.

        This function requires both wxPython and Pillow.

        @param nodes: elements to render.
        @param bounds: bounds of those elements for the viewport.
        @param width: desired width of the resulting raster
        @param height: desired height of the resulting raster
        @param bitmap: bitmap to use rather than provisioning
        @param step: raster step rate, int scale rate of the image.
        @param keepratio: get a picture with the same height / width
               ratio as the original
        @return:
        """
        if bounds is None:
            return None
        xxmin = float("inf")
        yymin = float("inf")
        xxmax = -float("inf")
        yymax = -float("inf")
        # print ("Recursion=%d" % recursion)
        if not isinstance(nodes, (tuple, list)):
            mynodes = [nodes]
        else:
            mynodes = nodes
        if recursion == 0:
            # Do it only once...
            textnodes = []
            for item in mynodes:
                if item.type == "elem text":
                    if item.text.width == 0 or item.text.height == 0:
                        textnodes.append(item)
            if len(textnodes) > 0:
                # print ("Invalid textnodes found, call me again...")
                self.make_raster(
                    nodes=textnodes,
                    bounds=bounds,
                    width=width,
                    height=height,
                    bitmap=bitmap,
                    step_x=step_x,
                    step_y=step_y,
                    keep_ratio=keep_ratio,
                    recursion=1,
                )

        for item in mynodes:
            bb = item.bounds
            # if item.type == "elem text":
            #     print ("Bounds for text: %.1f, %.1f, %.1f, %.1f, w=%.1f, h=%.1f)" % (bb[0], bb[1], bb[2], bb[3], item.text.width, item.text.height))
            if bb[0] < xxmin:
                xxmin = bb[0]
            if bb[1] < yymin:
                yymin = bb[1]
            if bb[2] > xxmax:
                xxmax = bb[2]
            if bb[3] > yymax:
                yymax = bb[3]

        xmin = xxmin
        ymin = yymin
        xmax = xxmax
        ymax = yymax
        xmax = ceil(xmax)
        ymax = ceil(ymax)
        xmin = floor(xmin)
        ymin = floor(ymin)
        # print ("Bounds: %.1f, %.1f, %.1f, %.1f, Mine: %.1f, %.1f, %.1f, %.1f)" % (xmin, ymin, xmax, ymax, xxmin, yymin, xxmax, yymax))

        image_width = int(xmax - xmin)
        if image_width == 0:
            image_width = 1

        image_height = int(ymax - ymin)
        if image_height == 0:
            image_height = 1

        if width is None:
            width = image_width
        if height is None:
            height = image_height
        # Scale physical image down by step amount.
        width /= float(step_x)
        height /= float(step_y)
        width = int(ceil(abs(width)))
        height = int(ceil(abs(height)))
        if width <= 0:
            width = 1
        if height <= 0:
            height = 1
        bmp = wx.Bitmap(width, height, 32)
        dc = wx.MemoryDC()
        dc.SelectObject(bmp)
        dc.SetBackground(wx.WHITE_BRUSH)
        dc.Clear()

        matrix = Matrix()
        matrix.post_translate(-xmin, -ymin)

        # Scale affine matrix up by step amount scaled down.
        scale_x = width / float(image_width)
        scale_y = height / float(image_height)
        if keep_ratio:
            scale_x = min(scale_x, scale_y)
            scale_y = scale_x
        matrix.post_scale(scale_x, scale_y)

        gc = wx.GraphicsContext.Create(dc)
        gc.SetInterpolationQuality(wx.INTERPOLATION_BEST)
        gc.PushState()
        if not matrix.is_identity():
            gc.ConcatTransform(
                wx.GraphicsContext.CreateMatrix(gc, ZMatrix(matrix)))
        if not isinstance(nodes, (list, tuple)):
            nodes = [nodes]
        gc.SetBrush(wx.WHITE_BRUSH)
        gc.DrawRectangle(xmin - 1, ymin - 1, xmax + 1, ymax + 1)
        self.render(nodes, gc, draw_mode=DRAW_MODE_CACHE | DRAW_MODE_VARIABLES)
        img = bmp.ConvertToImage()
        buf = img.GetData()
        image = Image.frombuffer("RGB", tuple(bmp.GetSize()), bytes(buf),
                                 "raw", "RGB", 0, 1)
        gc.PopState()
        dc.SelectObject(wx.NullBitmap)
        gc.Destroy()
        del dc
        if bitmap:
            return bmp

        # for item in mynodes:
        #     bb = item.bounds
        #     if item.type == "elem text":
        #         print ("Afterwards Bounds for text: %.1f, %.1f, %.1f, %.1f, w=%.1f, h=%.1f)" % (bb[0], bb[1], bb[2], bb[3], item.text.width, item.text.height))

        return image
Beispiel #17
0
 def calculate_matrices(self):
     self._matrix = Matrix(self.scene_to_device_matrix())
     self._imatrix = Matrix(self._matrix)
     self._imatrix.inverse()
Beispiel #18
0
 def aspect_matrix(self):
     """
     Specifically view the scene with the given Viewbox.
     """
     if self._frame and self._view and self.aspect:
         self.scene_widget.matrix = Matrix(self._view.transform(self._frame))
Beispiel #19
0
    def test_cutcode_image(self):
        """
        Convert CutCode from Image operation
        Test image-based crosshatched setting

        :return:
        """
        laserop = ImageOpNode()

        # Add Path
        initial = "M 0,0 L 100,100 L 0,0 M 50,-50 L 100,-100 M 0,0 Q 100,100 200,0"
        path = Path(initial)
        laserop.add_node(PathNode(path))

        # Add SVG Image1
        image = Image.new("RGBA", (256, 256), (255, 255, 255, 0))
        draw = ImageDraw.Draw(image)
        draw.ellipse((50, 50, 150, 150), "white")
        draw.ellipse((100, 100, 105, 105), "black")
        inode1 = ImageNode(image=image, matrix=Matrix(), dpi=1000.0 / 3.0)
        inode1.step_x = 3
        inode1.step_y = 3
        inode1.process_image()
        laserop.add_node(inode1)

        # Add SVG Image2
        image2 = Image.new("RGBA", (256, 256), (255, 255, 255, 0))
        draw = ImageDraw.Draw(image2)
        draw.ellipse((50, 50, 150, 150), "white")
        draw.ellipse((80, 80, 120, 120), "black")
        inode2 = ImageNode(image=image2, matrix=Matrix(), dpi=500, direction=4)
        inode2.step_x = 2
        inode2.step_y = 2
        inode2.process_image()
        laserop.add_node(inode2)  # crosshatch
        for i in range(2):  # Check for knockon
            cutcode = CutCode(laserop.as_cutobjects())
            self.assertEqual(len(cutcode), 3)

            rastercut = cutcode[0]
            self.assertTrue(isinstance(rastercut, RasterCut))
            self.assertEqual(rastercut.offset_x, 100)
            self.assertEqual(rastercut.offset_y, 100)
            image = rastercut.image
            self.assertTrue(isinstance(image, Image.Image))
            self.assertIn(image.mode, ("L", "1"))
            self.assertEqual(image.size, (2, 2))  # step value 2, 6/2
            self.assertEqual(
                rastercut.path, "M 100,100 L 100,106 L 106,106 L 106,100 Z"
            )

            rastercut1 = cutcode[1]
            self.assertTrue(isinstance(rastercut1, RasterCut))
            self.assertEqual(rastercut1.offset_x, 80)
            self.assertEqual(rastercut1.offset_y, 80)
            image1 = rastercut1.image
            self.assertTrue(isinstance(image1, Image.Image))
            self.assertIn(image1.mode, ("L", "1"))
            self.assertEqual(image1.size, (21, 21))  # default step value 2, 40/2 + 1
            self.assertEqual(rastercut1.path, "M 80,80 L 80,122 L 122,122 L 122,80 Z")

            rastercut2 = cutcode[2]
            self.assertTrue(isinstance(rastercut2, RasterCut))
            self.assertEqual(rastercut2.offset_x, 80)
            self.assertEqual(rastercut2.offset_y, 80)
            image2 = rastercut2.image
            self.assertTrue(isinstance(image2, Image.Image))
            self.assertIn(image2.mode, ("L", "1"))
            self.assertEqual(image2.size, (21, 21))  # default step value 2, 40/2 + 1
            self.assertEqual(rastercut2.path, "M 80,80 L 80,122 L 122,122 L 122,80 Z")
Beispiel #20
0
    def process_image(self):
        if self.step_x is None:
            step = UNITS_PER_INCH / self.dpi
            self.step_x = step
            self.step_y = step

        from PIL import Image, ImageEnhance, ImageFilter, ImageOps

        from meerk40t.image.actualize import actualize
        from meerk40t.image.imagetools import dither

        image = self.image
        main_matrix = self.matrix

        r = self.red * 0.299
        g = self.green * 0.587
        b = self.blue * 0.114
        v = self.lightness
        c = r + g + b
        try:
            c /= v
            r = r / c
            g = g / c
            b = b / c
        except ZeroDivisionError:
            pass
        if image.mode != "L":
            image = image.convert("RGB")
            image = image.convert("L", matrix=[r, g, b, 1.0])
        if self.invert:
            image = image.point(lambda e: 255 - e)

        # Calculate device real step.
        step_x, step_y = self.step_x, self.step_y
        if (
            main_matrix.a != step_x
            or main_matrix.b != 0.0
            or main_matrix.c != 0.0
            or main_matrix.d != step_y
        ):
            try:
                image, actualized_matrix = actualize(
                    image,
                    main_matrix,
                    step_x=step_x,
                    step_y=step_y,
                    inverted=self.invert,
                )
            except (MemoryError, DecompressionBombError):
                self.process_image_failed = True
                return
        else:
            actualized_matrix = Matrix(main_matrix)

        if self.invert:
            empty_mask = image.convert("L").point(lambda e: 0 if e == 0 else 255)
        else:
            empty_mask = image.convert("L").point(lambda e: 0 if e == 255 else 255)
        # Process operations.

        for op in self.operations:
            name = op["name"]
            if name == "crop":
                try:
                    if op["enable"] and op["bounds"] is not None:
                        crop = op["bounds"]
                        left = int(crop[0])
                        upper = int(crop[1])
                        right = int(crop[2])
                        lower = int(crop[3])
                        image = image.crop((left, upper, right, lower))
                except KeyError:
                    pass
            elif name == "edge_enhance":
                try:
                    if op["enable"]:
                        if image.mode == "P":
                            image = image.convert("L")
                        image = image.filter(filter=ImageFilter.EDGE_ENHANCE)
                except KeyError:
                    pass
            elif name == "auto_contrast":
                try:
                    if op["enable"]:
                        if image.mode not in ("RGB", "L"):
                            # Auto-contrast raises NotImplementedError if P
                            # Auto-contrast raises OSError if not RGB, L.
                            image = image.convert("L")
                        image = ImageOps.autocontrast(image, cutoff=op["cutoff"])
                except KeyError:
                    pass
            elif name == "tone":
                try:
                    if op["enable"] and op["values"] is not None:
                        if image.mode == "L":
                            image = image.convert("P")
                            tone_values = op["values"]
                            if op["type"] == "spline":
                                spline = ImageNode.spline(tone_values)
                            else:
                                tone_values = [q for q in tone_values if q is not None]
                                spline = ImageNode.line(tone_values)
                            if len(spline) < 256:
                                spline.extend([255] * (256 - len(spline)))
                            if len(spline) > 256:
                                spline = spline[:256]
                            image = image.point(spline)
                            if image.mode != "L":
                                image = image.convert("L")
                except KeyError:
                    pass
            elif name == "contrast":
                try:
                    if op["enable"]:
                        if op["contrast"] is not None and op["brightness"] is not None:
                            contrast = ImageEnhance.Contrast(image)
                            c = (op["contrast"] + 128.0) / 128.0
                            image = contrast.enhance(c)

                            brightness = ImageEnhance.Brightness(image)
                            b = (op["brightness"] + 128.0) / 128.0
                            image = brightness.enhance(b)
                except KeyError:
                    pass
            elif name == "gamma":
                try:
                    if op["enable"] and op["factor"] is not None:
                        if image.mode == "L":
                            gamma_factor = float(op["factor"])

                            def crimp(px):
                                px = int(round(px))
                                if px < 0:
                                    return 0
                                if px > 255:
                                    return 255
                                return px

                            if gamma_factor == 0:
                                gamma_lut = [0] * 256
                            else:
                                gamma_lut = [
                                    crimp(pow(i / 255, (1.0 / gamma_factor)) * 255)
                                    for i in range(256)
                                ]
                            image = image.point(gamma_lut)
                            if image.mode != "L":
                                image = image.convert("L")
                except KeyError:
                    pass
            elif name == "unsharp_mask":
                try:
                    if (
                        op["enable"]
                        and op["percent"] is not None
                        and op["radius"] is not None
                        and op["threshold"] is not None
                    ):
                        unsharp = ImageFilter.UnsharpMask(
                            radius=op["radius"],
                            percent=op["percent"],
                            threshold=op["threshold"],
                        )
                        image = image.filter(unsharp)
                except (KeyError, ValueError):  # Value error if wrong type of image.
                    pass
            elif name == "halftone":
                try:
                    if op["enable"]:
                        image = RasterScripts.halftone(
                            image,
                            sample=op["sample"],
                            angle=op["angle"],
                            oversample=op["oversample"],
                            black=op["black"],
                        )
                except KeyError:
                    pass

        if empty_mask is not None:
            background = Image.new(image.mode, image.size, "white")
            background.paste(image, mask=empty_mask)
            image = background  # Mask exists use it to remove any pixels that were pure reject.

        if self.dither and self.dither_type is not None:
            if self.dither_type != "Floyd-Steinberg":
                image = dither(image, self.dither_type)
            image = image.convert("1")
        inverted_main_matrix = Matrix(main_matrix).inverse()
        self.processed_matrix = actualized_matrix * inverted_main_matrix
        self.processed_image = image
        # self.matrix = actualized_matrix
        self.altered()
        self.process_image_failed = False
Beispiel #21
0
    def __init__(
        self,
        image=None,
        matrix=None,
        overscan=None,
        direction=None,
        dpi=500,
        operations=None,
        **kwargs,
    ):
        super(ImageNode, self).__init__(type="elem image", **kwargs)
        self.__formatter = "{element_type} {width}x{height}"
        if "href" in kwargs:
            self.matrix = Matrix()
            try:
                from PIL import Image as PILImage

                self.image = PILImage.open(kwargs["href"])
                if "x" in kwargs:
                    self.matrix.post_translate_x(kwargs["x"])
                if "y" in kwargs:
                    self.matrix.post_translate_x(kwargs["y"])
                real_width, real_height = self.image.size
                declared_width, declared_height = real_width, real_height
                if "width" in kwargs:
                    declared_width = kwargs["width"]
                if "height" in kwargs:
                    declared_height = kwargs["height"]
                try:
                    sx = declared_width / real_width
                    sy = declared_height / real_height
                    self.matrix.post_scale(sx, sy)
                except ZeroDivisionError:
                    pass
            except ImportError:
                self.image = None
        else:
            self.image = image
            self.matrix = matrix
        self.processed_image = None
        self.processed_matrix = None
        self.process_image_failed = False
        self.text = None

        self._needs_update = False
        self._update_thread = None
        self._update_lock = threading.Lock()

        self.settings = kwargs
        self.overscan = overscan
        self.direction = direction
        self.dpi = dpi
        self.step_x = None
        self.step_y = None
        self.lock = False

        self.invert = False
        self.red = 1.0
        self.green = 1.0
        self.blue = 1.0
        self.lightness = 1.0
        self.view_invert = False
        self.dither = True
        self.dither_type = "Floyd-Steinberg"

        if operations is None:
            operations = list()
        self.operations = operations
Beispiel #22
0
    def test_cutcode_image_crosshatch(self):
        """
        Convert CutCode from Image Operation.
        Test ImageOp Crosshatch Setting

        :return:
        """
        laserop = ImageOpNode(raster_direction=4)

        # Add Path
        initial = "M 0,0 L 100,100 L 0,0 M 50,-50 L 100,-100 M 0,0 Q 100,100 200,0"
        path = Path(initial)
        laserop.add_node(PathNode(path))

        # Add SVG Image1
        image1 = Image.new("RGBA", (256, 256), (255, 255, 255, 0))
        draw = ImageDraw.Draw(image1)
        draw.ellipse((50, 50, 150, 150), "white")
        draw.ellipse((100, 100, 105, 105), "black")
        inode = ImageNode(image=image1, matrix=Matrix(), dpi=1000.0 / 3.0)
        inode.step_x = 3
        inode.step_y = 3
        inode.process_image()
        laserop.add_node(inode)

        # Add SVG Image2
        image2 = Image.new("RGBA", (256, 256), (255, 255, 255, 0))
        draw = ImageDraw.Draw(image2)
        draw.ellipse((50, 50, 150, 150), "white")
        draw.ellipse((80, 80, 120, 120), "black")
        inode = ImageNode(image=image2, matrix=Matrix(), dpi=500.0)
        inode.step_x = 2
        inode.step_y = 2
        inode.process_image()
        laserop.add_node(inode)

        # Add SVG Image3
        inode = ImageNode(image=image2, matrix=Matrix(), dpi=1000.0 / 3.0)
        inode.step_x = 3
        inode.step_y = 3
        inode.process_image()
        laserop.add_node(inode)

        cutcode = CutCode(laserop.as_cutobjects())
        self.assertEqual(len(cutcode), 6)

        rastercut1_0 = cutcode[0]
        self.assertTrue(isinstance(rastercut1_0, RasterCut))
        self.assertEqual(rastercut1_0.offset_x, 100)
        self.assertEqual(rastercut1_0.offset_y, 100)
        image = rastercut1_0.image
        self.assertTrue(isinstance(image, Image.Image))
        self.assertIn(image.mode, ("L", "1"))
        self.assertEqual(image.size, (2, 2))  # step value 2, 6/2
        self.assertEqual(rastercut1_0.path, "M 100,100 L 100,106 L 106,106 L 106,100 Z")

        rastercut1_1 = cutcode[1]
        self.assertTrue(isinstance(rastercut1_1, RasterCut))
        self.assertEqual(rastercut1_1.offset_x, 100)
        self.assertEqual(rastercut1_1.offset_y, 100)
        image = rastercut1_1.image
        self.assertTrue(isinstance(image, Image.Image))
        self.assertIn(image.mode, ("L", "1"))
        self.assertEqual(image.size, (2, 2))  # step value 2, 6/2
        self.assertEqual(rastercut1_1.path, "M 100,100 L 100,106 L 106,106 L 106,100 Z")

        rastercut2_0 = cutcode[2]
        self.assertTrue(isinstance(rastercut2_0, RasterCut))
        self.assertEqual(rastercut2_0.offset_x, 80)
        self.assertEqual(rastercut2_0.offset_y, 80)
        image1 = rastercut2_0.image
        self.assertTrue(isinstance(image1, Image.Image))
        self.assertIn(image1.mode, ("L", "1"))
        self.assertEqual(image1.size, (21, 21))  # default step value 2, 40/2 + 1
        self.assertEqual(rastercut2_0.path, "M 80,80 L 80,122 L 122,122 L 122,80 Z")

        rastercut2_1 = cutcode[3]
        self.assertTrue(isinstance(rastercut2_1, RasterCut))
        self.assertEqual(rastercut2_1.offset_x, 80)
        self.assertEqual(rastercut2_1.offset_y, 80)
        image2 = rastercut2_1.image
        self.assertTrue(isinstance(image2, Image.Image))
        self.assertIn(image2.mode, ("L", "1"))
        self.assertEqual(image2.size, (21, 21))  # default step value 2, 40/2 + 1
        self.assertEqual(rastercut2_0.path, "M 80,80 L 80,122 L 122,122 L 122,80 Z")

        rastercut3_0 = cutcode[4]
        self.assertTrue(isinstance(rastercut3_0, RasterCut))
        self.assertEqual(rastercut3_0.offset_x, 80)
        self.assertEqual(rastercut3_0.offset_y, 80)
        image3 = rastercut3_0.image
        self.assertTrue(isinstance(image3, Image.Image))
        self.assertIn(image3.mode, ("L", "1"))
        self.assertEqual(image3.size, (14, 14))  # default step value 3, ceil(40/3) + 1
        self.assertEqual(rastercut3_0.path, "M 80,80 L 80,122 L 122,122 L 122,80 Z")

        rastercut3_1 = cutcode[5]
        self.assertTrue(isinstance(rastercut3_1, RasterCut))
        self.assertEqual(rastercut3_1.offset_x, 80)
        self.assertEqual(rastercut3_1.offset_y, 80)
        image4 = rastercut3_1.image
        self.assertTrue(isinstance(image4, Image.Image))
        self.assertIn(image4.mode, ("L", "1"))
        self.assertEqual(image4.size, (14, 14))  # default step value 3, ceil(40/3) + 1
        self.assertEqual(rastercut2_0.path, "M 80,80 L 80,122 L 122,122 L 122,80 Z")
Beispiel #23
0
    HITCHAIN_DELEGATE_AND_HIT,
    HITCHAIN_HIT,
    HITCHAIN_HIT_AND_DELEGATE,
    ORIENTATION_RELATIVE,
    RESPONSE_ABORT,
    RESPONSE_CHAIN,
    RESPONSE_CONSUME,
    RESPONSE_DROP,
)
from meerk40t.gui.scene.scenespacewidget import SceneSpaceWidget
from meerk40t.kernel import Job, Module
from meerk40t.svgelements import Matrix, Point

# TODO: _buffer can be updated partially rather than fully rewritten, especially with some layering.

_reused_identity_widget = Matrix()
XCELLS = 15
YCELLS = 15


class SceneToast:
    """
    SceneToast is drawn directly by the Scene. It creates an text message in a box that animates a fade.
    """
    def __init__(self, scene, left, top, right, bottom):
        self.scene = scene
        self.left = left
        self.top = top
        self.right = right
        self.bottom = bottom
        self.countdown = 0