Example #1
0
    def __init__(self, origin, data, box_size, version=None, error_correction=ERROR_CORRECT_M, border=0,
                 alignment='left-bottom'):
        self._qr_code = qrcode.QRCode(image_factory=ShapelyImageFactory, error_correction=error_correction,
                                      border=border, box_size=1, version=version)
        self._qr_code.add_data(data)
        self._alignment = Alignment(alignment)

        self.box_size = box_size
        self.origin = origin
Example #2
0
 def __init__(self, origin, height, text='', alignment='left-bottom', angle=0., font='stencil', line_spacing=1.5,
              true_bbox_alignment=False):
     self.origin = origin
     self.height = height
     self.text = str(text)
     self._alignment = Alignment(alignment)
     self.angle = normalize_phase(angle)
     self.font = font
     self.line_spacing = height * line_spacing
     self.true_bbox_alignment = true_bbox_alignment
     self._bbox = None
     self._shapely_object = None
Example #3
0
class QRCode(object):
    """
    Quick Response (QR) code part.

    This part is a simple wrapper around the qrcode library using the ShapelyImageFactory. If you need more
    flexibility this class might be extended or you can use the shapely output factory ShapelyImageFactory
    and qrcode directly.

    :param origin: Lower left corner of the QR code.
    :param data: Data which is to be encoded into the QR code.
    :param box_size: Size of each box.
    :param version: QR code version.
    :param error_correction: Level of error correction.
    :param border: Size of the surrounding free border.
    """
    # noinspection PyUnresolvedReferences
    from qrcode import ERROR_CORRECT_L, ERROR_CORRECT_H, ERROR_CORRECT_M, ERROR_CORRECT_Q

    def __init__(self, origin, data, box_size, version=None, error_correction=ERROR_CORRECT_M, border=0,
                 alignment='left-bottom'):
        self._qr_code = qrcode.QRCode(image_factory=ShapelyImageFactory, error_correction=error_correction,
                                      border=border, box_size=1, version=version)
        self._qr_code.add_data(data)
        self._alignment = Alignment(alignment)

        self.box_size = box_size
        self.origin = origin

    def get_shapely_object(self):
        self._qr_code.make(fit=True)
        shapely_code = self._qr_code.make_image(origin=self.origin, scale_factor=self.box_size)

        offset = self._alignment.calculate_offset(((0, 0), shapely_code.size))
        return shapely.affinity.translate(shapely_code.get_shapely_object(), *offset)
Example #4
0
    def add_to_row(self,
                   cell=None,
                   bbox=None,
                   alignment='left-bottom',
                   realign=True,
                   unique_id=None,
                   allow_region_layer=True):
        """
        Add a new cell to the row.

        The dimensions of the cell will either be determined directly or can be given via *bbox*. They determined
        bounding box always assumes a start at (0, 0) to preserve write field alignment.

        If *realign* is activated, the cell will be shifted, so that the it starts in the first write field quadrant.
        So if your cell contains structures as (-70, 10) and the write field size is 100, the cell will be shifted to
        (30, 10).

        The alignment of the cell can also be changed, but note that only 'left-bottom' guarantees write field
        alignment. When the region layers are used, these will force an alignment again, though.

        The position of each added cell inside the grid can be traced when passing a *unique_id* while adding the cell.

        :param cell: Cell to add.
        :type: gdsCAD.core.Cell
        :param bbox: Bounding box. If None, it will be determined based on *cell*.
        :param alignment: Alignment of the cell inside the grid.
        :param realign: Realign the cell, so that it starts in the first positive write field.
        :param unique_id: ID to trace where the cell was placed in the final layout.
        :param allow_region_layer: Allow a region layer around the cell.
        :type allow_region_layer: bool
        """
        assert self._current_row, 'Start a new row first'

        if cell is None:
            self._current_row['items'].append({
                'cell': None,
                'bbox': np.zeros([2, 2]),
                'offset': np.zeros(2),
                'id': unique_id,
                'alignment': Alignment(alignment),
                'allow_region_layer': False
            })
            return

        cell_bbox = np.reshape(cell.bounds, (2, 2)) if bbox is None else bbox

        if realign:
            offset = [
                self._remove_multiple_y_align(cell_bbox[0][i]) -
                cell_bbox[0][i] for i in (0, 1)
            ]
            cell_bbox += offset
        else:
            offset = (0, 0)

        if bbox is None:
            cell_bbox[0, :] = 0

        bbox = cell_bbox

        self._current_row['items'].append({
            'cell':
            cell,
            'bbox':
            bbox,
            'offset':
            offset,
            'id':
            unique_id,
            'alignment':
            Alignment(alignment),
            'allow_region_layer':
            allow_region_layer
        })
Example #5
0
 def test_calculate_offset(self):
     alignment = Alignment('left-bottom')
     self.assertTupleEqual(
         tuple(alignment.calculate_offset(((1, 2), (11, 12)))), (-1, -2))
     alignment = Alignment('center-bottom')
     self.assertTupleEqual(
         tuple(alignment.calculate_offset(((1, 2), (11, 12)))), (-6, -2))
     alignment = Alignment('right-center')
     self.assertTupleEqual(
         tuple(alignment.calculate_offset(((1, 2), (11, 12)))), (-11, -7))
     alignment = Alignment('right-top')
     self.assertTupleEqual(
         tuple(alignment.calculate_offset(((1, 2), (11, 12)))), (-11, -12))
Example #6
0
class Text:
    def __init__(self, origin, height, text='', alignment='left-bottom', angle=0., font='stencil', line_spacing=1.5,
                 true_bbox_alignment=False):
        self.origin = origin
        self.height = height
        self.text = str(text)
        self._alignment = Alignment(alignment)
        self.angle = normalize_phase(angle)
        self.font = font
        self.line_spacing = height * line_spacing
        self.true_bbox_alignment = true_bbox_alignment
        self._bbox = None
        self._shapely_object = None

    def _invalidate(self):
        self._bbox = None
        self._shapely_object = None

    @property
    def origin(self):
        return self._origin

    # noinspection PyAttributeOutsideInit
    @origin.setter
    def origin(self, origin):
        self._invalidate()
        self._origin = np.array(origin)
        assert self._origin.shape == (2,), 'origin not valid'

    @property
    def height(self):
        return self._height

    # noinspection PyAttributeOutsideInit
    @height.setter
    def height(self, height):
        self._invalidate()
        assert height > 0, 'Height must be positive'
        self._height = height

    @property
    def font(self):
        return self._font

    # noinspection PyAttributeOutsideInit
    @font.setter
    def font(self, font):
        self._invalidate()
        assert font in _fonts.FONTS, 'Font is "%s" unknown, must be one of %s' % (font, _fonts.FONTS.keys())
        self._font = font

    @property
    def alignment(self):
        return self._alignment.alignment

    @alignment.setter
    def alignment(self, alignment):
        self._invalidate()
        self._alignment = alignment

    @property
    def bounding_box(self):
        if self._bbox is None:
            self.get_shapely_object()

        return self._bbox

    def get_shapely_object(self):
        if not self.text:
            self._bbox = None
            return shapely.geometry.Polygon()

        if self._shapely_object:
            return self._shapely_object

        # Let's do the actual rendering

        polygons = list()

        special_handling_chars = '\n'
        font = _fonts.FONTS[self.font]

        # Check the text
        for char in self.text:
            if char in special_handling_chars:
                continue
            assert char in font, 'Character "%s" is not supported by font "%s"' % (char, self.font)

        max_x = 0
        cursor_x, cursor_y = 0, 0
        for i, char in enumerate(self.text):
            if char == '\n':
                cursor_x, cursor_y = 0, cursor_y - self.line_spacing
                continue

            char_font = font[char]
            cursor_x += char_font['width'] / 2 * self.height

            for line in char_font['lines']:
                points = np.array(line).T * self.height + (cursor_x, cursor_y)
                polygons.append(shapely.geometry.Polygon(points))

            # Add kerning
            if i < len(self.text) - 1 and self.text[i + 1] not in special_handling_chars:
                kerning = char_font['kerning'][self.text[i + 1]]
                cursor_x += (char_font['width'] / 2 + kerning) * self.height

            max_x = max(max_x, cursor_x + char_font['width'] / 2 * self.height)

        merged_polygon = shapely.ops.unary_union(polygons)

        # Handle the alignment, translation and rotation
        if not self.true_bbox_alignment:
            bbox = np.array([[0, max_x],
                             [cursor_y, self.height]]).T
        else:
            bbox = np.array(merged_polygon.bounds).reshape(2, 2)

        offset = self._alignment.calculate_offset(bbox)

        if not np.isclose(normalize_phase(self.angle), 0):
            aligned_text = shapely.affinity.translate(merged_polygon, *offset)
            rotated_text = shapely.affinity.rotate(aligned_text, self.angle, origin=[0, 0], use_radians=True)
            final_text = shapely.affinity.translate(rotated_text, *self.origin)
        else:
            final_text = shapely.affinity.translate(merged_polygon, *(offset + self.origin))

        self._bbox = np.array(final_text.bounds).reshape(2, 2)
        self._shapely_object = final_text
        return final_text