pygpibtoolkit/plotter/hpgl_parser.py

Thu, 24 May 2018 23:22:37 +0200

author
David Douard <david.douard@logilab.fr>
date
Thu, 24 May 2018 23:22:37 +0200
changeset 104
916c255b3079
parent 91
f2a8f688dbc0
permissions
-rw-r--r--

[plotter] flake8

# -*- coding: utf-8 -*-
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free Software
# Foundation; either version 2 of the License, or (at your option) any later
# version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details
#
# You should have received a copy of the GNU General Public License along with
# this program; if not, write to the Free Software Foundation, Inc.,
# 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
""" Copyright (c) 2007-2018 David Douard (Paris, FRANCE).
https://bitbucket.org/dddouard/pygpibtoolkit -- mailto:david.douard@sdfa3.orgx
"""

import re
import numpy

vsplitter = re.compile('[ ,]')
vextractor = re.compile(
    r'(?P<value>[^;\n\r\aA-DF-Za-df-z]*?)(?P<terminator>[;\n\r\a]+)',
    re.S + re.M)


class HPGLParser:
    def __init__(self, data=None):
        self.str_terminator = chr(0x03)
        self.IN()
        if data:
            self.parse(data)

    def parse(self, data):
        self.data = data
        self.idx = 0
        self.IN()
        while self.idx < len(data):
            while data[self.idx] in ';\n\r\a ':
                self.idx += 1
            if data[self.idx] == chr(0x03):
                self.ESC()
            else:
                cmd = data[self.idx: self.idx+2].upper()
                self.idx += 2
                try:
                    getattr(self, cmd)()
                except AttributeError:
                    print("WARNING: received unknown command %s" % repr(cmd))

            while self.idx < len(data) and data[self.idx] in ';\n\r\a ':
                self.idx += 1

    def user_to_abs(self, pos):
        """
        converts a position (x, y) given in user units to absolute
        units
        """
        return pos  # TODO

    def extract_value(self, raw=False):
        m = vextractor.match(self.data[self.idx:])
        if m:
            val = m.group("value")
            self.idx = self.idx + len(val) + len(m.group("terminator"))
            val = vsplitter.split(val)
            if not raw:
                val = [eval(x) for x in val if x.strip()]
            return val
        return []

    def extract_string(self):
        idx2 = self.data.find(self.str_terminator, self.idx)
        val = self.data[self.idx: idx2]
        self.idx = idx2+1
        return val

    def polyline(self, values):
        if (len(values) % 2) == 1:
            # this is a syntax error in the HPGL data
            values = values[:-1]
        values = numpy.array(values).reshape(-1, 2)
        # TODO use scaling
        # do plots
        values = numpy.r_[[self.pos], values]  # add self.pos as 1st value
        if self.plot_mode == 'relative':
            values = values.cumsum(axis=0)
        if self.symbol_mode:
            self.plot_symbols(values[1:])
        if self.pen_state == "down":
            self.plot_lines(values)
        self.pos = values[-1]

    def plot_string(self, s):
        pass

    def get_window_for_paper(self):
        return (0, 100, 0, 100)

    def get_scaling_points_for_paper(self):
        return (0, 100, 0, 100)

    # effective plot methods; to be defined in subclasses (backends)
    def plot_symbols(self, points):
        pass

    def plot_lines(self, points):
        pass

    # HPGL-related methods
    def ESC(self):
        self.idx += 1

    def OE(self):
        """ Output Error """
        pass

    def EC(self):
        """? what's this """
        # values = self.extract_value()
        pass

    def OO(self):
        """idem"""
        pass

    def OP(self):
        """idem"""
        pass

    def VS(self):
        """idem"""
        pass

    def DF(self):
        """ Default """
        self.pen = 0
        self.pen_width = 1  # 1/300 inch
        self.pos = (0, 0)
        self.char_set = "std"
        self.plot_mode = 'absolute'
        self.char_direction = (1, 0)
        self.line_type = 0  # 'solid'
        self.pattern_len = 4  # percentage
        self.window = self.get_window_for_paper()
        self.char_size = (0.75, 1.5)  # percentages
        self.symbol_mode = False
        self.tick_len = (0.5, 0.5)  # %
        self.std_char_set = 0
        self.alt_char_set = 0
        self.selected_char_set = 0
        self.char_slant = 0  # degrees
        self.scale = None
        self.str_terminator = chr(0x03)
        self.chord_ang = 5  # degrees
        self.fill_type = ('bidirectionnal', 1)
        self.fill_distance = 1  # %
        self.fill_slant = 0  # degrees
        self.pen_thickness = 0.3  # mm

    def IN(self):
        """ Initialize """
        self.DF()
        self.pen_state = "up"
        self.rotation = 0  # degrees
        self.scaling_points = self.get_scaling_points_for_paper()

    def IP(self):
        """ Input Scale Point"""
        values = self.extract_value()
        if len(values) == 0:
            self.scaling_points = self.get_scaling_points_for_paper()
        elif len(values) == 2:
            p1x, p1y, p2x, p2y = self.scaling_points
            dx, dy = p2x-p1x, p2y-p1y
            x, y = values
            self.scaling = (x, y, x+dx, y+dy)
        elif len(values) == 4:
            self.values = tuple(values)

    def SC(self):
        """ Scale """
        values = self.extract_value()
        if len(values) == 4:
            self.scale = tuple(values)
        else:
            self.scale = None

    def IW(self):
        values = self.extract_value()
        if len(values) == 0:
            self.window = self.get_window_for_paper()
        elif len(values) == 4:
            self.window = tuple(values)

    def RO(self):
        values = self.extract_value()
        if len(values) == 0:
            self.rotation = 0
        elif len(values) == 1:
            self.rotation = values[0]

    def PG(self):
        pass

    def PU(self):
        """ Pen Up"""
        self.pen_state = "up"
        values = self.extract_value()
        self.polyline(values)

    def PD(self):
        """ Pen Down """
        self.pen_state = "down"
        values = self.extract_value()
        self.polyline(values)

    def PA(self):
        """ Plot Absolute """
        self.plot_mode = "absolute"
        values = self.extract_value()
        self.polyline(values)

    def PR(self):
        """ Plot Relative """
        self.plot_mode = "relative"
        values = self.extract_value()
        self.polyline(values)

    def AA(self):
        """ Arc Absolute """
        values = self.extract_value()
        if len(values) in [3, 4]:
            x, y, qc = values[:3]
            if len(values) == 4:
                qd = values[-1]  # noqa
            else:
                qd = self.chord_ang  # noqa
            # TODO : plot arc
            # print("plotting an arc")

    def AR(self):
        """ Arc Relative """
        # values = self.extract_value()
        # TODO

    def CI(self):
        """ Circle Plot"""
        # values = self.extract_value()
        # TODO

    def EA(self):
        """Edge Rectangle Absolute"""
        # values = self.extract_value()
        # TODO

    def ER(self):
        """Edge Rectangle Relative"""
        # values = self.extract_value()
        # TODO

    def EW(self):
        """ Edge Wedge """
        # values = self.extract_value()
        # TODO

    def RA(self):
        """ Fill Rectangle Absolute """
        # values = self.extract_value()
        # TODO

    def RR(self):
        """ Fill Rectangle Relative """
        # values = self.extract_value()
        # TODO

    def WG(self):
        """ Fill Wedge """
        # values = self.extract_value()
        # TODO

    def FT(self):
        """ Fill Type """
        # values = self.extract_value()
        # TODO

    def LT(self):
        """ Line Type """
        values = self.extract_value()

        if len(values) == 0:
            self.line_type = 0  # 'solid'
        else:
            self.line_type = values[0]
            if len(values) > 1:
                self.pattern_len = values[1]

    def PW(self):
        """ Pen Width """
        values = self.extract_value()
        if len(values) == 1:
            self.pen_thickness = values[0]

    def SM(self):
        """ Symbol Mode """
        values = self.extract_value()
        if len(values) == 0:
            self.symbol_mode = False
        elif len(values) == 1:
            self.symbol_mode = values[0]

    def SP(self):
        """ Select Pen """
        values = self.extract_value()
        if len(values) == 1:
            self.pen = values[0]

    def TL(self):
        """ Tick Len """
        # values = self.extract_value()
        # TODO

    def XT(self):
        """ X-axis Tick """
        # TODO
        # print("X ticks")
        pass

    def YT(self):
        """ X-axis Tick """
        # TODO
        # print("Y ticks")
        pass

    def PT(self):
        """ Pen Thickness Select """
        values = self.extract_value()
        if len(values) == 0:
            self.pen_thickness = 0.3
        elif len(values) == 1:
            self.pen_thickness = values[0]

    def CS(self):
        """ Standard Character Set """
        values = self.extract_value()
        if len(values) == 0:
            self.std_char_set = 0
        elif len(values) == 1:
            self.std_char_set = values[0]

    def CA(self):
        """ Alternate Character Set """
        values = self.extract_value()
        if len(values) == 0:
            self.alt_char_set = 0
        elif len(values) == 1:
            self.alt_char_set = values[0]

    def SS(self):
        """ Select Standard Character Set """
        self.char_set = "std"

    def SA(self):
        """ Select Alternate Character Set """
        self.char_set = "alt"

    def DT(self):
        """ Define Label Terminator """
        values = self.extract_value(raw=True)
        if len(values) == 0:
            self.str_terminator = chr(0x03)
        elif len(values) == 1:
            self.str_terminator = values[0]

    def LB(self):
        """ Character Plot """
        values = self.extract_string()
        self.plot_string(values)
        x, y = self.pos
        values = values.split('\n')
        if self.char_size == "absolute":
            x += len(values[-1]) * self.char_width * 1016 / 2.54
            y += (len(values)-1) * self.char_height * 1016 / 2.54
        else:
            x0, x1, y0, y1 = self.scale
            dx = x1-x0
            dy = y1-y0
            x += len(values[-1]) * self.char_width / 100.0 * dx
            y += (len(values)-1) * self.char_height / 100.0 * dy

        self.pos = [x, y]

    def get_char_size(self):
        if self.char_size == "absolute":
            x = self.char_width * 1016 / 2.54
            y = self.char_height * 1016 / 2.54
        else:
            x0, x1, y0, y1 = self.scale
            dx = x1-x0
            dy = y1-y0
            x = self.char_width / 100.0 * dx
            y = self.char_height / 100.0 * dy
        return x, y

    def DI(self):
        """ Absolute Direction """
        values = self.extract_value()
        if len(values) == 0:
            self.char_direction = (1.0, 0.0)
        elif len(values) == 2:
            self.char_direction = values

    def DR(self):
        """ Relative Direction """
        values = self.extract_value()
        if len(values) == 0:
            self.char_direction = (1.0, 0.0)
        elif len(values) == 2:
            # TODO : compute as percentages
            self.char_direction = values

    def CP(self):
        """ Character Plot """
        values = self.extract_value()
        # TODO
        if len(values) == 0:
            values = 0, 1
        x, y = self.pos
        if self.char_size == "absolute":
            x += values[0] * self.char_width * 1016 / 2.54
            y += values[1] * self.char_height * 1016 / 2.54
        else:
            x0, x1, y0, y1 = self.scale
            dx = x1-x0
            dy = y1-y0
            x += values[0] * self.char_width / 100.0 * dx
            y += values[1] * self.char_height / 100.0 * dy
        self.pos = [x, y]

    def SI(self):
        """ Set Absolute Character Size """
        values = self.extract_value()
        self.char_size = "absolute"
        if len(values) == 0:
            self.char_width = 0.1879  # cm
            self.char_height = 0.2690  # cm
        elif len(values) == 2:
            self.char_width, self.char_height = values

    def SR(self):
        """ Set Relative Character Size """
        values = self.extract_value()
        self.char_size = "relative"
        if len(values) == 0:
            self.char_width = 0.75  # percentage
            self.char_height = 1.5  # id
        elif len(values) == 2:
            self.char_width, self.char_height = values

    def SL(self):
        """ Character Slant """
        # values = self.extract_value()
        # TODO
        pass

    def UC(self):
        """ User Defined Character """
        # values = self.extract_value()
        # TODO
        pass


if __name__ == '__main__':
    import sys
    data = open(sys.argv[1]).read()

    p = HPGLParser(data)

mercurial