Esempio n. 1
0
 def bugmark(self, P):
     P = P - Point(1, 1)
     style = basestyle.Duplicate()
     style.fill_pattern = SolidPattern(StandardColors.black)
     style.line_pattern = SolidPattern(StandardColors.black)
     self.prop_stack.AddStyle(style)
     self.rectangle(2, 0, 0, 2, P.x, P.y)
Esempio n. 2
0
 def try_add_style(self, key, val):
     if key == 'fill':
         if val == 'none':
             self.style.fill_pattern = EmptyPattern
         else:
             color = csscolor(val)
             self._print('fill', color)
             self.style.fill_pattern = SolidPattern(color)
     elif key == 'stroke':
         if val == 'none':
             self.style.line_pattern = EmptyPattern
         else:
             color = csscolor(val)
             self._print('stroke', color)
             self.style.line_pattern = SolidPattern(color)
     elif key == 'stroke-width':
         width = self.user_length(val)
         # Multiply the width with a value taken from the
         # transformation matrix because so far transforming an
         # object in Sketch does not affect the stroke width in any
         # way. Thus we have to do that explicitly here.
         # FIXME: using m11 is not really the best approach but in
         # many cases better than using the width as is.
         width = self.trafo.m11 * width
         self._print('width', width)
         self.style.line_width = width
     elif key == 'stroke-linejoin':
         self.style.line_join = join[val]
     elif key == 'stroke-linecap':
         self.style.line_cap = cap[val]
     elif key == 'font-family':
         try:
             # convert val to 8bit string.
             self.style.font = GetFont(str(val))
         except UnicodeError:
             # If it's not ASCII we probably won't have the font, so
             # use the default one.
             # FIXME: Give a warning
             pass
     elif key == 'font-size':
         self.style.font_size = self.user_length(val)
         ####self.style.font_size = float(val)
     elif key == 'text-anchor':
         if val == 'start':
             self.halign = text.ALIGN_LEFT
         elif val == 'middle':
             self.halign = text.ALIGN_CENTER
         elif val == 'end':
             self.halign = text.ALIGN_RIGHT
Esempio n. 3
0
 def ls(self):
     style = self.style
     style.line_pattern = SolidPattern(self.line_color)
     style.line_width = self.line_width
     style.line_join = self.line_join
     style.line_cap = self.line_cap
     style.line_dashes = self.line_dashes
Esempio n. 4
0
def getStdConnLine(fromobj, toobj, cplist=()):
    global tarrw1, tarrw2, tarrw3
    #~ snAdct['ptfl_1'],	# from (startpoint - moveto)
    #~ snBdct['box'],		# to (endpoint)
    #~ # here optional arguments for 'in-between' points;
    #~ # (ordered) tuple of dict, where key is command:
    #~ # like in tikz:: '|' means along y  ,  '-' means along x
    #~ # (last {'-':-10}, is not needed - endpoint specifies it
    #~ ({'-':10}, {'|':-20}, {'-':-30}, {'|':-40})

    # for now, we expect that fromobj is always going to be a
    # 'pointer' tf line; and to obj is going to be a box

    connlineCol = SolidPattern(CreateRGBColor(0.3, 0.3, 0.3))

    # retrieve start point - endpoint of fromobj ptf line
    # get 2nd node (=segment 1) - in there, Point() is third in list ([2])
    tmpep_start = fromobj.paths[0].Segment(1)[2]

    # NOTE though: 'skpoint' object has only read-only attributes !! (cannot assign to .x)

    # retrieve end point - the center left (west) point of the box of to obj:
    #~ tmpep_end = toobj.bounding_rect.center()
    #~ tmpep_end.x = toobj.bounding_rect.left
    # there seems to be a 10 units padding for bounding_rect; (see below)
    # compensate it
    tobr = toobj.bounding_rect.grown(-10)
    tmpep_end = Point(tobr.left, tobr.center().y)

    # start drawing the line

    tpath = CreatePath()

    tpath.AppendLine(tmpep_start)  # moveto

    # are there any 'in-between' connection points ?
    prevPoint = tmpep_start
    nextPoint = tmpep_start
    for ibcp in cplist:
        axiscommand = ibcp.keys()[0]
        moveval = ibcp[axiscommand]
        if axiscommand == '-':  # along x
            #~ nextPoint.x = prevPoint.x + moveval
            nextPoint = Point(prevPoint.x + moveval, prevPoint.y)
        elif axiscommand == '|':  # along y
            #~ nextPoint.y = prevPoint.y + moveval
            nextPoint = Point(prevPoint.x, prevPoint.y + moveval)
        tpath.AppendLine(nextPoint)  # moveto
        prevPoint = nextPoint

    tpath.AppendLine(tmpep_end)  # lineto

    tline = PolyBezier((tpath, ))
    #~ tline.AddStyle(tbase_style) # of Graphics.properties (also in compound, document) - seems to add a 'layer' if dynamic; else seems to 'replace' ?!
    tline.SetProperties(line_width=2.0,
                        line_pattern=connlineCol,
                        line_arrow2=tarrw2)
    tline.update_rects()

    return tline
Esempio n. 5
0
 def setfillstyle(self):
     style = basestyle.Duplicate()
     if reff.fill.type == 1:
         style.fill_pattern = SolidPattern(
             apply(CreateRGBColor, reff.fill.color))
     elif reff.fill.type == 3:
         style.fill_pattern = HatchingPattern(
             apply(CreateRGBColor, reff.fill.color), StandardColors.white,
             Point(2.0, 1.0), 5, 1)
     if reff.edge.visible:
         style.line_pattern = SolidPattern(
             apply(CreateRGBColor, reff.edge.color))
         style.line_width = reff.edge.width
         if reff.edge.widthmode == 0:
             style.line_width = style.line_width * self.Scale
         style.line_dashes = reff.edge.dashtable[reff.edge.type - 1]
     self.prop_stack.AddStyle(style)
Esempio n. 6
0
 def setlinestyle(self):
     style = basestyle.Duplicate()
     style.line_pattern = SolidPattern(
         apply(CreateRGBColor, reff.line.color))
     style.line_width = reff.line.width
     if reff.line.widthmode == 0:
         style.line_width = style.line_width * self.Scale
     style.line_dashes = reff.line.dashtable[reff.line.type - 1]
     self.prop_stack.AddStyle(style)
Esempio n. 7
0
    def __init__(self, master=None, **kw):
        apply(PatternFrame.__init__, (self, master), kw)

        label = Label(self, text = _("Color"))
        label.pack(side = LEFT, expand = 1, anchor = E)
        self.color_but = ColorButton(self, width = 3, height = 1,
                                     command = self.set_color)
        self.color_but.pack(side = LEFT, expand = 1, anchor = W)

        self.SetPattern(SolidPattern(StandardColors.black))
Esempio n. 8
0
    def CreateBrushIndirect(self):
        style, r, g, b, hatch = self.get_struct('<hBBBxh')
        if style == 1:
            pattern = EmptyPattern
        else:
            pattern = SolidPattern(
                CreateRGBColor(r / 255.0, g / 255.0, b / 255.0))
        self.add_gdiobject((('fill_pattern', pattern), ))

        self._print('->', style, r, g, b, hatch)
Esempio n. 9
0
    def ta_set_colour(self, colour):

        if tuple(colour) == (255, 255, 255):

            return EmptyPattern
        else:
            return SolidPattern(
                CreateRGBColor(
                    float(colour[0]) / 255.0,
                    float(colour[1]) / 255.0,
                    float(colour[2]) / 255.0))
Esempio n. 10
0
 def do_apply(self):
     kw = {}
     if not self.var_color_none.get():
         color = self.color_but.Color()
         kw["line_pattern"] = SolidPattern(color)
         kw["line_width"] = self.var_width.get()
         kw["line_join"] = self.opt_join.GetValue()
         kw["line_cap"] = self.opt_cap.GetValue()
         kw["line_dashes"] = self.opt_dash.GetValue()
         kw["line_arrow1"] = self.opt_arrow1.GetValue()
         kw["line_arrow2"] = self.opt_arrow2.GetValue()
     else:
         kw["line_pattern"] = EmptyPattern
     self.set_properties(_("Set Outline"), 'line', kw)
Esempio n. 11
0
    def change_color(self, object):
        if object.has_properties:
            properties = object.Properties()
            pattern = properties.fill_pattern
            if pattern.is_Solid:
                if pattern.Color() == self.color:
                    pattern = SolidPattern(black)
                else:
                    pattern = SolidPattern(white)
                undo = properties.SetProperty(fill_pattern=pattern)
                self.undo.append(undo)

            pattern = properties.line_pattern
            if pattern.is_Solid:
                if pattern.Color() == self.color:
                    pattern = SolidPattern(black)
                else:
                    pattern = SolidPattern(white)
                undo = properties.SetProperty(line_pattern=pattern)
                self.undo.append(undo)
Esempio n. 12
0
def getQuickLine(tstart, tend):
	# expected tuple at input

	pstart = Point(tstart[0], tstart[1])
	pend = Point(tend[0], tend[1])
	tpath = CreatePath()

	# Note - apparently, the first appended point is "moveTo";
	# .. the ubsequent ones being "LineTo"
	tpath.AppendLine(pstart) # moveto
	tpath.AppendLine(pend) # lineto

	tline = PolyBezier((tpath,))
	tline.AddStyle(tbase_style) # of Graphics.properties (also in compound, document) - seems to add a 'layer' if dynamic; else seems to 'replace' ?!
	tline.SetProperties(line_pattern = SolidPattern(CreateRGBColor(0.7, 0.7, 0.9)))
	return tline
Esempio n. 13
0
 def CreatePenIndirect(self):
     style, widthx, widthy, r, g, b = self.get_struct('<hhhBBBx')
     cap = (style & 0x0F00) >> 8
     join = (style & 0x00F0) >> 4
     style = style & 0x000F
     if style == 5:
         pattern = EmptyPattern
     else:
         pattern = SolidPattern(
             CreateRGBColor(r / 255.0, g / 255.0, b / 255.0))
     width = abs(widthx * self.trafo.m11)
     self.add_gdiobject(((
         'line_pattern',
         pattern,
     ), ('line_width', width)))
     self._print('->', style, widthx, widthy, r, g, b, cap, join)
Esempio n. 14
0
def create_text(context):
    # Create the text 'xyzzy' at 100,100. The first parameter to the
    # constructor is an affine transformation.
    text = SimpleText(Translation(100, 100), "xyzzy")
    
    # Set the font to 36pt Times-Bold and fill with solid green.
    # The text object is modified by this method, but the text object is
    # not yet part of the document, so we don't have to deal with undo
    # here.
    text.SetProperties(fill_pattern = SolidPattern(StandardColors.green),
                       font = GetFont('Times-Bold'),
                       font_size = 36)
    # Finally, insert the text object at the top of the current layer
    # and select it. Like all public document methods that modify the
    # document, the Insert method takes care of undo information itself.
    context.document.Insert(text)
Esempio n. 15
0
 def get_pattern(self, color, style=None):
     if style == -1:
         return EmptyPattern
     rgb = self.colors[color]
     if style is not None:
         if color in (BLACK, DEFAULT_COLOR):
             if style > 0 and style <= 20:
                 rgb = Blend(self.colors[WHITE], rgb, (20 - style) / 20.0)
             elif style == 0:
                 rgb = self.colors[WHITE]
         else:
             if style >= 0 and style < 20:
                 rgb = Blend(self.colors[BLACK], rgb, (20 - style) / 20.0)
             elif style > 20 and style <= 40:
                 rgb = Blend(self.colors[WHITE], rgb, (style - 20) / 20.0)
     return SolidPattern(rgb)
Esempio n. 16
0
 def __init__(self, loader):
     self.loader = loader
     self.trafo = self.basetrafo = Trafo()
     self.state_stack = ()
     self.style = loader.style.Copy()
     self.style.line_pattern = EmptyPattern
     self.style.fill_pattern = SolidPattern(StandardColors.black)
     self.current_text = ""
     self.style.font = GetFont("Times-Roman")
     self.style.font_size = 12
     self.halign = text.ALIGN_LEFT
     self.named_objects = {}
     self.in_defs = 0
     self.paths = None
     self.path = None
     self.depth = 0
     self.indent = '    '
Esempio n. 17
0
 def TEXT(self, size):
     P = self.Pnt()
     F = self.Enum()
     S = self.getstr()
     T = Translation(self.trafo(P))
     Py = Point(reff.text.orientation[0]).normalized()
     Px = Point(reff.text.orientation[1]).normalized()
     B = transform_base(Point(0.0, 0.0), reff.text.expansion * Px, Py)
     self.style = basestyle.Duplicate()
     self.style.font = GetFont(fntlst[self.fntmap[reff.text.fontindex]])
     self.style.font_size = reff.text.height * self.Scale
     self.style.fill_pattern = SolidPattern(
         apply(CreateRGBColor, reff.text.color))
     O = text.SimpleText(text=S,
                         trafo=T(B),
                         halign=text.ALIGN_LEFT,
                         valign=text.ALIGN_BASE,
                         properties=self.get_prop_stack())
     self.append_object(O)
Esempio n. 18
0
elp.trafo = Trafo(elp_radius, 0, 0, elp_radius, elp_startx, elp_starty)
elpbz = elp.AsBezier()
elpbz_nl = elpbz.Paths()[0].NodeList() # as list of Point(x,y)
elpbz_tnl = [] # as list of tuples (x,y)
for tpnt in elpbz_nl:
	elpbz_tnl.append( (tpnt.x, tpnt.y) )
tarrw1 = Arrow(elpbz_tnl, closed=1) #_nl or _tnl - all the same here; ellipse sends four points, which in Arrow are AppendBezier (AppendLine only for two points -- but still it looks like a diamond.. )..
# the difference is in tarrw1.Paths()[0].arc_lengths() vs elpbz.Paths()[0].arc_lengths()
tarrw1.path = elpbz.Paths()[0] # and this FINALLY makes the arrow a circle!
tarrw2 = Arrow(arpath2, closed=1)

global tbase_style
tbase_style = Style()
tbase_style.fill_pattern = EmptyPattern
tbase_style.fill_transform = 1
tbase_style.line_pattern = SolidPattern(StandardColors.red)
tbase_style.line_width = 2.0
tbase_style.line_join = const.JoinMiter
tbase_style.line_cap = const.CapButt
tbase_style.line_dashes = ()
tbase_style.line_arrow1 = tarrw1
tbase_style.line_arrow2 = tarrw2
tbase_style.font = None
tbase_style.font_size = 12.0

# from create_spiral.py
from Sketch import CreatePath

# from footprints.py
from Sketch import Group
Esempio n. 19
0
 def DibCreatePatternBrush(self):
     self.add_message(_("Bitmap brushes not yet implemented. Using black"))
     pattern = SolidPattern(StandardColors.black)
     self.add_gdiobject((('fill_pattern', pattern), ))
Esempio n. 20
0
def main():
    import Sketch
    global doc, psfile
    global tbase_style, courfont

    Sketch.init_lib()

    draw_printable = 1
    draw_visible = 0
    embed_fonts = 0
    eps_for = util.get_real_username()
    eps_date = util.current_date()
    eps_title = None
    rotate = 0

    doc = Document(create_layer=1)

    ## *****************************
    # start of drawing

    # indicate start of coord system first: + goes upward / rightward
    trec = getQuickRect(5, 5, -10, -10)
    doc.Insert(trec)

    # from create_text.py
    textfld = SimpleText(Translation(0, 0), "24pt")
    textfld.SetProperties(font=courfont, font_size=24)
    doc.Insert(textfld)

    ## add struct box nodes

    structNodeA = getStructBoxnode(
        "struct1",  # title label
        ("*pnt1", "*pointer2", "*point3"),
        ("var1", "variable2", "Varib3"))
    structNodeB = getStructBoxnode(
        "str2",  # title label
        (
            "*pnt1",
            "*pnt2",
            "*pnt3",
            "*pnt4",
            "*pnt5",
            "*pnt6",
        ),
        ("var1", "var2", "var3", "var4", "var5", "var6", "var7", "var8",
         "var9"))
    structNodeC = getStructBoxnode(
        "str3",  # title label
        ("*pnt1", ),
        ())
    snAgrp = structNodeA[0]
    snBgrp = structNodeB[0]
    snCgrp = structNodeC[0]

    ## position nodes

    # place node A
    snAgrp.Translate(Point(100, 200))
    snAgrp.update_rects()

    # place node B - 'below left' of node A
    # (i.e. find lower right corner of A group;
    #   align upper left corner of B with that)
    sabr = snAgrp.bounding_rect
    sbbr = snBgrp.bounding_rect
    xtran = sabr.right - sbbr.left
    ytran = sabr.bottom - sbbr.top
    snBgrp.Translate(Point(xtran, ytran))
    snBgrp.update_rects()
    sbbr = snBgrp.bounding_rect

    # place node C - below, left aligned to node B
    scbr = snCgrp.bounding_rect
    xtran = sbbr.left - scbr.left
    ytran = sbbr.bottom - scbr.top
    snCgrp.Translate(Point(xtran, ytran))
    snCgrp.update_rects()
    scbr = snCgrp.bounding_rect

    # show struct nodes on drawing
    doc.Insert(snAgrp)
    doc.Insert(snBgrp)
    doc.Insert(snCgrp)

    # display the bounding boxes of struct nodes
    trecA = getQuickRect(sabr.left, sabr.bottom, sabr.right - sabr.left,
                         sabr.top - sabr.bottom)
    trecA.SetProperties(line_pattern=SolidPattern(StandardColors.red))
    trecB = getQuickRect(sbbr.left, sbbr.bottom, sbbr.right - sbbr.left,
                         sbbr.top - sbbr.bottom)
    trecB.SetProperties(line_pattern=SolidPattern(StandardColors.red))
    trecC = getQuickRect(scbr.left, scbr.bottom, scbr.right - scbr.left,
                         scbr.top - scbr.bottom)
    trecC.SetProperties(line_pattern=SolidPattern(StandardColors.red))
    doc.Insert(trecA)
    doc.Insert(trecB)
    doc.Insert(trecC)

    # make a connection
    # get dicts first
    snAdct = structNodeA[1]
    snBdct = structNodeB[1]
    snCdct = structNodeC[1]

    # put a rect around snBdct['box']
    # since grown(-10) aligns to box; shows that theres 10 units padding!
    tbr = snBdct['box'].bounding_rect.grown(-10)
    trecC = getQuickRect(tbr.left, tbr.bottom, tbr.right - tbr.left,
                         tbr.top - tbr.bottom)
    trecC.SetProperties(line_pattern=SolidPattern(StandardColors.green))
    doc.Insert(trecC)

    # for easier calc after:
    yjump = abs(snAdct['ptfl_1'].bounding_rect.center().y -
                snBdct['box'].bounding_rect.center().y) / 2

    # 'standard' connection:
    # start from 2nd [1] pointer of structNodeA - end in 'input' (left) of box of structNodeB
    # so, from dict we need:: structNodeA:'ptfl_1'; and structNodeB:'box'
    tconnline = getStdConnLine(
        snAdct['ptfl_1'],  # from (startpoint - moveto)
        snBdct['box'],  # to (endpoint)
        # here optional arguments for 'in-between' points;
        # (ordered) tuple of dict, where key is command:
        # like in tikz:: '|' means along y  ,  '-' means along x
        # (last {'-':-10}, is not needed - endpoint specifies it
        ({
            '-': 30
        }, {
            '|': -yjump
        }, {
            '-': -40
        }, {
            '|': -yjump
        }))
    doc.Insert(tconnline)

    # for easier calc after -
    # we won't use the "half-the-distance" directly, so we don't divide /2 here
    yjumpAC = abs(snAdct['ptfl_2'].bounding_rect.center().y -
                  snCdct['box'].bounding_rect.center().y)

    tconnlineAC = getStdConnLine(
        snAdct['ptfl_2'],  # from (startpoint - moveto)
        snCdct['box'],  # to (endpoint)
        ({
            '-': 20
        }, {
            '|': -(yjump - 30)
        }, {
            '-': -40
        }, {
            '|': -(yjumpAC - (yjump - 30))
        }))
    doc.Insert(tconnlineAC)

    ## *****************************
    # end of drawing - generate output file

    filename = ''  # was .sk filename to convert
    sk2ps(filename,
          thisfilename,
          printable=draw_printable,
          visible=draw_visible,
          For=eps_for,
          CreationDate=eps_date,
          Title=eps_title,
          rotate=rotate,
          embed_fonts=embed_fonts)
Esempio n. 21
0
 def lp(self, color=None):
     if color is None:
         self.style.line_pattern = self.pattern
     else:
         self.style.line_pattern = SolidPattern(self.convert_color(color))
Esempio n. 22
0
 def set_color(self):
     self.pattern = SolidPattern(self.__get_color())
     self.issue(CHANGED)
Esempio n. 23
0
 def DropAt(self, x, y, what, data):
     if what == DROP_COLOR:
         self.init_from_pattern(SolidPattern(data))
Esempio n. 24
0
    def recompute(self):
        chunker = Chunker(rstrip(self.text), self.styling, '\t')

        objects = []
        options = {}
        x = 0
        y = 0
        maxy = 0
        line = []
        while not chunker.eof():
            text, styles = chunker.get()

            family = styles['family'].options
            color = styles['color'].options
            fill = SolidPattern(getattr(StandardColors, color))
            # XXX should be done in advance

            size = styles['size'].options
            bold = styles['bold'].options
            italic = styles['italic'].options

            if bold and italic:
                attr = [
                    'Bold Italic', 'Bold Oblique', 'Demi Oblique',
                    'Demi Italic', 'Medium Italic', 'Demi Bold Italic'
                ]
            elif bold:
                attr = ['Bold', 'Demi', 'Medium', 'Demi Bold']
            elif italic:
                attr = [
                    'Italic', 'Oblique', 'Book Oblique', 'Light Italic',
                    'Regular Oblique', 'Regular Italic'
                ]
            else:
                attr = ['Roman', 'Book', 'Light', 'Regular']

            supersub = styles['supersub'].options
            if len(text) > 0:
                height = 1.2 * size  # XXX is that ok ?
                if maxy < height:
                    maxy = height

                if supersub != 'normal':
                    if supersub == 'superscript':
                        offset = size * 0.5
                    else:
                        offset = -size * 0.15
                    size = 0.5 * size
                else:
                    offset = 0
                textObj = SimpleText(Translation(x, offset), text)
                objects.append(textObj)
                line.append(textObj)

                psfont = FONTS.ps(family=family, attr=attr)
                font = GetFont(psfont)

                textObj.SetProperties(font=font,
                                      font_size=size,
                                      fill_pattern=fill)

                left, bottom, right, top = textObj.coord_rect
                width = right - left

            else:
                width = 0

            reason = chunker.reason()
            if reason == '\n' or chunker.eof():
                if width > 0:
                    y = y - maxy
                else:
                    y = y - size
                maxy = 0
                x = 0
                for obj in line:
                    obj.Transform(self.trafo(Translation(0, y)))
                line = []
            elif reason == '\t':
                x = x + width + size
                #
                # XXX this is very provisional
                x = ceil(x / 50.) * 50.
            else:
                x = x + width

        # XXX this is a problem: what happens, if the MultilineText onbject
        #           is empty  ??
        #~ if len(objects)>0:
        self.set_objects(objects)
Esempio n. 25
0
def main():
	import Sketch
	global doc
	global tbase_style

	Sketch.init_lib()

	draw_printable = 1
	draw_visible = 0
	embed_fonts = 0
	eps_for = util.get_real_username()
	eps_date = util.current_date()
	eps_title = None
	rotate = 0


	#doc = load.load_drawing('')
	# from mainwindow.py: self.SetDocument(Document(create_layer = 1))
	doc = Document(create_layer = 1)

	# get font info first
	Graphics.font.read_font_dirs()

	# indicate start of coord system first
	# coord system:: + goes upward / rightward
	# from RectangleCreator: trafo = Trafo(off.x, 0, 0, off.y, end.x, end.y)
	# actually, there 'end' seems to correspond to start (llc: lower left corner of rectangle) - and 'off' to the length extended in each direction (i.e. width, height - but can be negative) ; so instead of 'end' - calling it 'start'
	start_x = 5
	start_y = 5
	off_x = -10
	off_y = -10
	trec = Rectangle(trafo = Trafo(off_x, 0, 0, off_y, start_x, start_y))
	trec.update_rects()
	doc.Insert(trec)


	# from create_text.py
	textfld = SimpleText(Translation(50, 50), "xyzzy")
	textfld.SetProperties(fill_pattern = SolidPattern(StandardColors.green),
					   font = GetFont('Courier-Bold'),#('Times-Bold'),
					   font_size = 36)

	#copy textfld
	textfld2 = textfld.Duplicate()
	textfld2.SetProperties(fill_pattern = SolidPattern(StandardColors.blue)) # change color only

	# rotate textfld
	angleDeg = 45
	angleRad = pi*(angleDeg/180.0) # ensure float op - could use math.radians instead
	textfld.Transform(Rotation(angleRad)) # Rotation(angle, center)
	textfld.update_rects() # probably a good idea

	# change textfld's text with the current width (that we see)
	# get bounding box of text
	a = textfld.properties
	llx, lly, urx, ury = a.font.TextBoundingBox(textfld.text, a.font_size)
	# calculate width - its of UNTRANSFORMED text
	twidth = urx - llx
	# insert this width as text in textbox now:
	#~ textfld.text = str(twidth)
	#~ textfld.update_rects() # probably a good idea - again

	# get textfield as bezier
	textbez = textfld.AsBezier()
	#~ print textbez # returns Sketch.Graphics.group.Group; subclass of EditableCompound
	# the bounding rectangle - from Compound (type is Rect):
	textbez_bRect = textbez.bounding_rect
	# calc width now
	t2width = textbez_bRect.right - textbez_bRect.left
	# insert this width as text in textbox now:
	textfld.text = str(t2width)
	textfld.update_rects() # probably a good idea - again

	#~ doc.Insert(textfld)

	# create a line
	# using create_spiral.py technique below (see syntax note #(A1))
	tpath = CreatePath()

	# Note - apparently, the first appended point is "moveTo";
	# .. the ubsequent ones being "LineTo"
	tp = Point(textbez_bRect.left,textbez_bRect.bottom)
	tpath.AppendLine(tp) # moveto

	tp = Point(textbez_bRect.left,textbez_bRect.top)
	tpath.AppendLine(tp) # lineto
	tp = Point(textbez_bRect.right,textbez_bRect.top)
	tpath.AppendLine(tp) # lineto
	tp = Point(textbez_bRect.right,textbez_bRect.bottom)
	tpath.AppendLine(tp) # lineto

	tline = PolyBezier((tpath,))
	tline.AddStyle(tbase_style) # of Graphics.properties (also in compound, document) - seems to add a 'layer' if dynamic; else seems to 'replace' ?!

	#~ doc.Insert(tline)

	# group tline and textfld ...
	# footprints.py has Group(foot_prints = [])
	tgrp = Group([textfld, textfld2, tline])
	tgrp.update_rects()
	doc.Insert(tgrp)

	# add a box.. around textfld2
	# use radius1, radius2 !=  0 AND 1 (logarithmic) to get RoundedRectangle (best between 0.0 and 1.0)
	tfbr = textfld2.bounding_rect
	start_x = tfbr.left
	start_y = tfbr.bottom
	off_x = tfbr.right - tfbr.left
	off_y = tfbr.top - tfbr.bottom
	twid = abs(off_x - start_x)
	thei = abs(off_y - start_y)
	radfact = 1.2*twid/thei
	tradius = 0.05 # if we want to specify a single one, then the actual look will depend on the dimesions of the rectangle - so must 'smooth' it with radfact...
	trec = Rectangle(trafo = Trafo(off_x, 0, 0, off_y, start_x, start_y), radius1 = tradius, radius2 = tradius*radfact)
	trec.update_rects()
	doc.Insert(trec)

	# add another box - any where
	start_x = 100.0
	start_y = 100.0
	off_x = 50.0
	off_y = 50.0
	trec2 = Rectangle(trafo = Trafo(off_x, 0, 0, off_y, start_x, start_y))
	trec2.update_rects()
	doc.Insert(trec2)

	# try change props post insert - OK
	trec2.SetProperties(fill_pattern = SolidPattern(StandardColors.yellow), line_width = 2.0, line_pattern = SolidPattern(CreateRGBColor(0.5, 0.5, 0.7)))

	# try move the group as a whole (Translate - syntax: spread.py)
	# say, align the right edge of tline to left edge of trec2 (x direction)
	# NOTE: group does not define own .AsBezier(self);
	# but it has tgrp.bounding_rect (although python doesn't show it in dir(tgrp))
	# also there is Rectangle.bounding_rect
	# NOTE though - it seems bounding_rect is somehow padded, with (at least) 10 units in each direction! (also, bounding rect of line will include the arrow)
	xmove = (trec2.bounding_rect.left+10)-(tline.bounding_rect.right-10)
	#~ print xmove, trec2.bounding_rect.left, tline.bounding_rect.right
	tgrp.Translate(Point(xmove, 0))
	tgrp.update_rects()

	# add temporary line to indicate bounding boxes
	# and effect of padding (may cover the very first trec)
	tmpbr = trec2.bounding_rect
	doc.Insert(
		getQuickLine(
			(0,0),
			(trec2.bounding_rect.left+10, tline.bounding_rect.top-10)
		)
	)

	# end of draw  - generate output file
	filename = ''
	psfile = 'vectorout.ps'
	sk2ps(filename, psfile, printable= draw_printable, visible = draw_visible,
		  For = eps_for, CreationDate = eps_date, Title = eps_title,
		  rotate = rotate, embed_fonts = embed_fonts)
Esempio n. 26
0
 def fs(self):
     if self.gradient_geo:
         pattern = self.make_gradient_pattern()
     else:
         pattern = SolidPattern(self.fill_color)
     self.style.fill_pattern = pattern
Esempio n. 27
0
 def ps(self, color):
     self.pattern = SolidPattern(self.convert_color(color))
Esempio n. 28
0
    def read_objects(self, objects):

        n_objects = 0

        # Traverse the list of drawfile object
        for object in objects:

            if isinstance(object, drawfile.group):

                # Start a group object in the document
                self.begin_group()

                # Descend into the group
                n_objects_lower = self.read_objects(object.objects)

                # If the group was empty then don't try to end it
                if n_objects_lower == 0:
                    #                    self.__pop()
                    (self.composite_class, self.composite_args,
                     self.composite_items,
                     self.composite_stack) = self.composite_stack
                else:
                    # End group object
                    self.end_group()
                    n_objects = n_objects + 1

            elif isinstance(object, drawfile.tagged):

                # Tagged object
                n_objects_lower = self.read_objects([object.object])

                if n_objects_lower != 0:
                    n_objects = n_objects + 1

            elif isinstance(object, drawfile.path):

                # Path object
                n_objects = n_objects + 1

                # Set the path style
                self.style.line_width = object.width / scale

                if object.style['join'] == 'mitred':
                    self.style.line_join = const.JoinMiter

                if object.style['start cap'] == 'butt':
                    self.style.line_cap = const.CapButt

                elif object.style['start cap'] == 'round':

                    if object.width > 0:
                        width = 0.5
                        length = 0.5
                    else:
                        width = 0.0
                        length = 0.0

                    # Draw arrow
                    path = [(0.0, width),
                            (0.5 * length, width, length, 0.5 * width, length,
                             0.0),
                            (length, -0.5 * width, 0.5 * length, -width, 0.0,
                             -width), (0.0, width)]

                    self.style.line_arrow1 = Arrow(path, 1)

                elif object.style['start cap'] == 'square':

                    if object.width > 0:
                        width = 0.5
                        length = 0.5
                    else:
                        width = 0.0
                        length = 0.0

                    # Draw arrow
                    path = [(0.0, width), (length, width), (length, -width),
                            (0.0, -width), (0.0, width)]

                    self.style.line_arrow1 = Arrow(path, 1)

                elif object.style['start cap'] == 'triangular':

                    if object.width > 0:
                        width = object.style['triangle cap width'] / 16.0
                        length = object.style['triangle cap length'] / 16.0
                    else:
                        width = 0.0
                        length = 0.0

                    # Draw arrow
                    path = [(0.0, width), (length, 0.0), (0.0, -width),
                            (0.0, width)]

                    self.style.line_arrow1 = Arrow(path, 1)
                    if (object.width / scale) < 1.0:
                        self.style.line_arrow1.path.Transform(
                            Scale(object.width / scale, object.width / scale))

                if object.style['end cap'] == 'butt':
                    self.style.line_cap = const.CapButt

                elif object.style['end cap'] == 'round':

                    if object.width > 0:
                        width = 0.5
                        length = 0.5
                    else:
                        width = 0.0
                        length = 0.0

                    # Draw arrow
                    path = [(0.0, width),
                            (0.5 * length, width, length, 0.5 * width, length,
                             0.0),
                            (length, -0.5 * width, 0.5 * length, -width, 0.0,
                             -width), (0.0, width)]

                    self.style.line_arrow2 = Arrow(path, 1)

                elif object.style['end cap'] == 'square':

                    if object.width > 0:
                        width = 0.5
                        length = 0.5
                    else:
                        width = 0.0
                        length = 0.0

                    # Draw arrow
                    path = [(0.0, width), (length, width), (length, -width),
                            (0.0, -width), (0.0, width)]

                    self.style.line_arrow2 = Arrow(path, 1)

                elif object.style['end cap'] == 'triangular':

                    if object.width > 0:
                        width = object.style['triangle cap width'] / 16.0
                        length = object.style['triangle cap length'] / 16.0
                    else:
                        width = 0.0
                        length = 0.0

                    # Draw arrow
                    path = [(0.0, width), (length, 0.0), (0.0, -width),
                            (0.0, width)]

                    self.style.line_arrow2 = Arrow(path, 1)
                    if (object.width / scale) < 1.0:
                        self.style.line_arrow2.path.Transform(
                            Scale(object.width / scale, object.width / scale))

                # Outline colour
                if object.outline == [255, 255, 255, 255]:
                    self.style.line_pattern = EmptyPattern
                else:
                    self.style.line_pattern = SolidPattern(
                        CreateRGBColor(
                            float(object.outline[1]) / 255.0,
                            float(object.outline[2]) / 255.0,
                            float(object.outline[3]) / 255.0))

                # Fill colour
                if object.fill == [255, 255, 255, 255]:
                    self.style.fill_pattern = EmptyPattern
                else:
                    self.style.fill_pattern = SolidPattern(
                        CreateRGBColor(
                            float(object.fill[1]) / 255.0,
                            float(object.fill[2]) / 255.0,
                            float(object.fill[3]) / 255.0))

                # Dash pattern
                if object.style['dash pattern'] == 'present':
                    line_dashes = []
                    for n in object.pattern:

                        line_dashes.append(int(n / scale))

                    self.style.line_dashes = tuple(line_dashes)

                # Create a list of path objects in the document
                paths = []
                path = None

                # Examine the path elements
                for element in object.path:

                    if element[0] == 'move':

                        x, y = self.relative(element[1][0], element[1][1])

                        # Add any previous path to the list
                        if path != None:
                            #                            path.load_close()
                            paths.append(path)

                        path = CreatePath()
                        path.AppendLine(x, y)

                    elif element[0] == 'draw':

                        x, y = self.relative(element[1][0], element[1][1])
                        path.AppendLine(x, y)

                    elif element[0] == 'bezier':

                        x1, y1 = self.relative(element[1][0], element[1][1])
                        x2, y2 = self.relative(element[2][0], element[2][1])
                        x, y = self.relative(element[3][0], element[3][1])
                        path.AppendBezier(x1, y1, x2, y2, x, y)

                    elif element[0] == 'close':

                        path.ClosePath()

                    elif element[0] == 'end':

                        # Should be the last object in the path
                        #                        path.load_close()
                        paths.append(path)
                        break

                # Create a bezier object
                if paths != []:
                    self.bezier(tuple(paths))

            elif isinstance(object, drawfile.font_table):

                # Font table
                n_objects = n_objects + 1

                # Set object level instance
                self.font_table = object.font_table

            elif isinstance(object, drawfile.text):

                # Text object
                n_objects = n_objects + 1

                # Determine the font
                if self.font_table.has_key(object.style):
                    self.style.font = RISCOSFont(self.font_table[object.style])
                else:
                    self.style.font = GetFont('Times Roman')

                # The size
                self.style.font_size = object.size[0] / scale

                # Outline colour
                if object.background == [255, 255, 255, 255]:
                    self.style.line_pattern = EmptyPattern
                else:
                    self.style.line_pattern = SolidPattern(
                        CreateRGBColor(
                            float(object.background[1]) / 255.0,
                            float(object.background[2]) / 255.0,
                            float(object.background[3]) / 255.0))

                # Fill colour
                if object.foreground == [255, 255, 255, 255]:
                    self.style.fill_pattern = EmptyPattern
                else:
                    self.style.fill_pattern = SolidPattern(
                        CreateRGBColor(
                            float(object.foreground[1]) / 255.0,
                            float(object.foreground[2]) / 255.0,
                            float(object.foreground[3]) / 255.0))

                # Transformation
                if hasattr(object, 'transform'):
                    x, y = object.transform[4] / scale, object.transform[
                        5] / scale
                    ox, oy = self.relative(object.baseline[0],
                                           object.baseline[1])
                    transform = Trafo(object.transform[0] / 65536.0,
                                      object.transform[1] / 65536.0,
                                      object.transform[2] / 65536.0,
                                      object.transform[3] / 65536.0, ox + x,
                                      oy + y)
                else:
                    transform = Translation(
                        self.relative(object.baseline[0], object.baseline[1]))

                # Write the text
                self.simple_text(object.text, transform)

            elif isinstance(object, drawfile.jpeg):

                # JPEG object
                n_objects = n_objects + 1

                # Transformation matrix
                x, y = self.relative(object.transform[4], object.transform[5])

                # Scale the object using the dpi information available, noting
                # that unlike Draw which uses 90 dpi, Sketch uses 72 dpi.
                # (I assume this since 90 dpi Drawfile JPEG objects appear 1.25
                # times larger in Sketch if no scaling is performed here.)
                scale_x = (object.transform[0] / 65536.0) * (72.0 /
                                                             object.dpi_x)
                scale_y = (object.transform[3] / 65536.0) * (72.0 /
                                                             object.dpi_y)

                transform = Trafo(scale_x, object.transform[1] / 65536.0,
                                  object.transform[2] / 65536.0, scale_y, x, y)

                # Decode the JPEG image
                image = Image.open(StringIO.StringIO(object.image))

                #                # Read dimensions of images in pixels
                #                width, height = image.size
                #
                #                # Divide these by the dpi values to obtain the size of the
                #                # image in inches
                #                width, height = width/float(object.dpi_x), \
                #                height/float(object.dpi_y)

                #                image.load()
                self.image(image, transform)

            elif isinstance(object, drawfile.sprite):

                # Sprite object
                n_objects = n_objects + 1

                # Transformation matrix

                if hasattr(object, 'transform'):
                    x, y = self.relative(object.transform[4],
                                         object.transform[5])

                    # Multiply the scale factor by that in the transformation matrix
                    scale_x = (object.transform[0]/65536.0) * \
                              (72.0 / object.sprite['dpi x'])
                    scale_y = (object.transform[3]/65536.0) * \
                              (72.0 / object.sprite['dpi y'])

                    transform = Trafo( scale_x,
                                       (object.transform[1]/65536.0) * \
                                       (72.0 / object.sprite['dpi y']),
                                       (object.transform[2]/65536.0) * \
                                       (72.0 / object.sprite['dpi x']),
                                       scale_y,
                                       x, y )
                else:
                    x, y = self.relative(object.x1, object.y1)

                    # Draw scales the Sprite to fit in the object's
                    # bounding box. To do the same, we need to know the
                    # actual size of the Sprite
                    # In points:
                    #                    size_x = 72.0 * float(object.sprite['width']) / \
                    #                                           object.sprite['dpi x']
                    #                    size_y = 72.0 * float(object.sprite['height']) / \
                    #                                           object.sprite['dpi y']
                    #
                    #                    # Bounding box dimensions in points:
                    #                    bbox_width = (object.x2 - object.x1)/scale
                    #                    bbox_height = (object.y2 - object.y1)/scale
                    #
                    #                    # Scale factors
                    #                    scale_x = (bbox_width / size_x) * \
                    #                               (72.0 / object.sprite['dpi x'])
                    #                    scale_y = (bbox_height / size_y) * \
                    #                               (72.0 / object.sprite['dpi y'])
                    scale_x = (object.x2 - object.x1) / \
                              (scale * object.sprite['width'])
                    scale_y = (object.y2 - object.y1) / \
                              (scale * object.sprite['height'])

                    transform = Trafo(scale_x, 0.0, 0.0, scale_y, x, y)

                # Create an Image object
                image = Image.fromstring(
                    object.sprite['mode'],
                    (object.sprite['width'], object.sprite['height']),
                    object.sprite['image'])

                self.image(image, transform)

            elif isinstance(object, drawfile.options):

                # Options object
                n_objects = n_objects + 1

                # Read page size
                paper_size = object.options['paper size']
                orientation = object.options['paper limits']
                if paper_size in papersizes:

                    if orientation == 'landscape':
                        self.page_layout = pagelayout.PageLayout(
                            object.options['paper size'],
                            orientation=pagelayout.Landscape)
                    else:
                        self.page_layout = pagelayout.PageLayout(
                            object.options['paper size'],
                            orientation=pagelayout.Portrait)

                if object.options['grid locking'] == 'on':

                    spacing = object.options['grid spacing']
                    if object.options['grid units'] == 'in':
                        spacing = spacing * 72.0
                    else:
                        spacing = spacing * 72.0 / 2.54

                    if object.options['grid shown'] == 'on':
                        visible = 1
                    else:
                        visible = 0

#                    self.begin_layer_class( GridLayer,
#                                (
#                                    (0, 0, int(spacing), int(spacing)),
#                                    visible,
#                                    CreateRGBColor(0.0, 0.0, 0.0),
#                                    _("Grid")
#                                ) )
#                    self.end_composite()

            elif isinstance(object, drawfile.text_area):

                # Text area
                n_objects = n_objects + 1

                # The text area object contains a number of columns.
                self.columns = len(object.columns)

                # Start in the first column and move to subsequent
                # columns as required, unless the number is overidden
                # by details in the text area.
                self.column = 0

                # The cursor position is initially undefined.
                cursor = [None, None]

                # The column margins
                self.margin_offsets = [1.0, 1.0]
                self.margins = [ (object.columns[self.column].x1 / scale) + \
                                 self.margin_offsets[0],
                                 (object.columns[self.column].x2 / scale) - \
                                 self.margin_offsets[1] ]

                # The column base
                self.column_base = object.columns[self.column].y1 / scale

                # Line and paragraph spacing
                self.linespacing = 0.0
                paragraph = 10.0

                # Current font name and dimensions
                font_name = ''
                font_size = 0.0
                font_width = 0.0

                # Text colours
                background = (255, 255, 255)
                foreground = (0, 0, 0)

                # Build lines (lists of words) until the column width
                # is reached then write the line to the page.
                line = []
                width = 0.0

                # Current text alignment
                align = 'L'

                # Last command to be executed
                last_command = ''

                # Execute the commands in the text area:
                for command, args in object.commands:

                    if command == '!':
                        # Version number
                        #                        print 'Version number', args
                        pass

                    elif command == 'A':
                        #                        print 'Align:', args
                        # Write current line
                        self.ta_write_line(align, cursor, line, 0)
                        # Empty the line list
                        line = []
                        # Set the line width
                        width = 0.0
                        # Align text
                        align = args
                        # Start new line
                        cursor = self.ta_new_line(cursor, object,
                                                  self.linespacing)

                    elif command == 'B':
                        #                        print 'Background:', args
                        # Background colour
                        background = args

                    elif command == 'C':
                        #                        print 'Foreground:', args
                        # Foreground colour
                        foreground = args

                    elif command == 'D':
                        #                        print 'Columns:', args
                        # Number of columns
                        if self.column == 0 and cursor == [None, None]:
                            # Nothing rendered yet, so change number of columns
                            self.columns = args

                    elif command == 'F':
                        #                        print 'Define font:', args
                        # Define font (already defined in object.font_table)
                        pass

                    elif command == 'L':
                        #                        print 'Line spacing:', args
                        # Set line spacing
                        self.linespacing = args

                    elif command == 'M':
                        #                        print 'Margins:', args
                        # Change margins
                        self.margin_offsets = [args[0], args[1]]
                        self.margins = [
                            (object.columns[self.column].x1 / scale) + args[0],
                            (object.columns[self.column].x2 / scale) - args[1]
                        ]

                    elif command == 'P':
                        #                        print 'Paragraph spacing:', args
                        # Change paragraph spacing
                        paragraph = args

                    elif command == 'U':
                        #                        print 'Underlining'
                        # Underlining
                        pass

                    elif command == 'V':
                        #                        print 'Vertical displacement'
                        # Vertical displacement
                        pass

                    elif command == '-':
                        #                        print 'Hyphen'
                        # Hyphen
                        pass

                    elif command == 'newl':

                        #                        print 'New line'
                        # New line
                        # Write current line
                        self.ta_write_line(align, cursor, line, 0)
                        # Start new line
                        cursor = self.ta_new_line(cursor, object,
                                                  self.linespacing)

                        # Can't position cursor?
                        if cursor == [None, None]:
                            break

                        # Empty the line list
                        line = []
                        # Set the line width
                        width = 0.0

                    elif command == 'para':

                        #                        print 'New paragraph'
                        # New paragraph
                        # Write current line
                        self.ta_write_line(align, cursor, line, 0)
                        # Start new line
                        if last_command != 'newl':
                            cursor = self.ta_new_line(
                                cursor, object, paragraph + self.linespacing)
                        else:
                            cursor = self.ta_new_line(cursor, object,
                                                      paragraph)

                        # Can't position cursor?
                        if cursor == [None, None]:
                            break

                        # Empty the line list
                        line = []
                        # Set the line width
                        width = 0.0

                    elif command == ';':
                        #                        print 'Comment:', args
                        # Comment
                        pass

                    elif command == 'font':
                        #                        print 'Use font:', args
                        # Font change
                        font_name, \
                                   font_size, \
                                   font_width = object.font_table[args]
                        # Select font
                        use_font = RISCOSFont(font_name)
                        # Move cursor to start of a line if the cursor is
                        # undefined
                        if cursor == [None, None]:
                            cursor[0] = self.margins[0]
                            cursor[1] = (object.columns[self.column].y2 /
                                         scale) - font_size
                        # Set line spacing
                        self.linespacing = font_size

                    elif command == 'text':

                        #                        print args
                        # Text. Add it to the line, checking that the line
                        # remains within the margins.
                        text, space = self.make_safe(args[0]), args[1]

                        # Add the width of the text to the current total width
                        width = width + \
                                use_font.TextCoordBox(text, font_size)[2]

                        #                        print width, margins[1] - margins[0]

                        # Compare current total width with column width
                        while width > (self.margins[1] - self.margins[0]):

                            # First write any text on this line
                            if line != []:

                                # Width will exceed column width
                                #                                print 'Width will exceed column width'
                                # Write current line
                                self.ta_write_line(align, cursor, line, 1)
                                # Start new line
                                cursor = self.ta_new_line(
                                    cursor, object, self.linespacing)

                                # Can't position cursor?
                                if cursor == [None, None]:
                                    break

                                # Clear the list
                                line = []
                                # Reset the width
                                width = 0.0

                            # Now attempt to fit this word on the next line
                            width = use_font.TextCoordBox(text, font_size)[2]

                            br = len(text)
                            # Continue to try until the word fits, or none of it fits
                            while width > (self.margins[1] - self.margins[0]) \
                                  and br > 0:

                                # Keep checking the size of the word
                                width = use_font.TextCoordBox(
                                    text[:br], font_size)[2]
                                br = br - 1

                            if br == 0:
                                # Word couldn't fit in the column at all, so
                                # break out of this loop
                                break

                            elif br < len(text):
                                # Write the subword to the line
                                self.ta_write_line(
                                    align, cursor,
                                    [(text[:br], font_name,
                                      font_size, font_width,
                                      self.ta_set_colour(foreground),
                                      self.ta_set_colour(background))], 0)

                                # Start new line
                                cursor = self.ta_new_line(
                                    cursor, object, self.linespacing)

                                # Can't position cursor?
                                if cursor == [None, None]:
                                    break

                                # keep the remaining text
                                text = text[br:]
                                # The width is just the width of this text
                                width = use_font.TextCoordBox(text,
                                                              font_size)[2]

                            # If the whole string fit onto the line then
                            # control will flow to the else clause which will
                            # append the text to the line list for next time.
                        else:
                            # The text fits within the margins so add the text
                            # to the line
                            line.append(
                                (text, font_name, font_size, font_width,
                                 self.ta_set_colour(foreground),
                                 self.ta_set_colour(background)))

                            # Also append any trailing space
                            if space != '':
                                line.append(
                                    (space, font_name, font_size, font_width,
                                     self.ta_set_colour(foreground),
                                     self.ta_set_colour(background)))
                                width = width + \
                                        use_font.TextCoordBox(
                                            space, font_size)[2]

                        # Can't position cursor?
                        if cursor == [None, None]:
                            break

                    # Remember this command
                    last_command = command

                # Render any remaining text
                if line != [] and cursor != [None, None]:

                    # Write current line
                    self.ta_write_line(align, cursor, line, 0)

            else:
                pass

        # Return the number of recognised objects
        return n_objects
Esempio n. 29
0
def getStructBoxnode(inlabel, plist, vlist):
    global courfont
    global tarrw1, tarrw2, tarrw3
    pvfnsz = 12  # font size for pointers/vars

    # here we'll also generate a dict of all graphic objects
    # global 'id' is not needed - it will be a part of a tuple, along with master group
    sbdict = {}

    ## pointers - horizontal
    yoff = 0
    ypad = 2
    lastcharpad = pvfnsz  # so the arrow dont overlap with last char
    xoutermost = 0  # to get the longest arrow (so we use it to make arrows same length)
    point_tflist = []  # list of actual textfield objects
    tcount = 0
    for iptxt in plist:
        textfld = SimpleText(Translation(0, yoff), iptxt)
        textfld.SetProperties(font=courfont, font_size=pvfnsz)
        textfld.update_rects()
        sbdict['ptf_' + str(tcount)] = textfld
        # add connector line
        tfbr = textfld.bounding_rect
        tfline = getQuickLine((tfbr.left, tfbr.bottom),
                              (tfbr.right + lastcharpad, tfbr.bottom))
        tfline.SetProperties(line_width=2.0,
                             line_arrow1=tarrw1,
                             line_arrow2=tarrw2)
        tfline.update_rects()
        sbdict['ptfl_' + str(tcount)] = tfline
        if tfbr.right + lastcharpad > xoutermost:
            xoutermost = tfbr.right + lastcharpad
        # group line and text
        tgrp = Group([tfline, textfld])
        tgrp.update_rects()
        sbdict['ptfg_' + str(tcount)] = tgrp
        # add the group - not just textfld - to list here
        point_tflist.append(tgrp)
        # get height - calc next offset; yoff will go negative, but nvm (will group and move)
        # don't use tgrp's BB, its too high
        llx, lly, urx, ury = textfld.bounding_rect  # /tgrp. /getTfBB(textfld)
        tfHeight = ury - lly
        yoff -= tfHeight + ypad
        tcount += 1
    # done - now resize all the arrows according to xoutermost,
    # so they are same length
    # SetLine(i, x, y[, cont]) "Replace the ith segment by a line segment"
    # where the i-th segment is the node..
    for tgrp in point_tflist:  # loop (in a list)
        tfline = tgrp.objects[
            0]  # access Group (sub)object - tfline is first [0]
        # get 2nd node (=segment 1) - in there, Point() is third in list ([2])
        tmpep = tfline.paths[0].Segment(1)[2]
        # change 2nd node (=segment 1) of the (first) path (paths[0])
        tfline.paths[0].SetLine(1, (xoutermost, tmpep.y))
        tfline.update_rects()
        tgrp.update_rects()
    # finally, group all these
    point_group = Group(point_tflist)  # accepts list directly
    point_group.update_rects()
    sbdict['ptg'] = point_group

    ## variables - vertical (so, rotate group afterwards)
    yoff = 0
    ypad = 2
    lastcharpad = 1  # pvfnsz /so the arrow dont overlap with last char
    xoutermost = 0  # to get the longest arrow (so we use it to make arrows same length)
    varbl_tflist = []  # list of actual textfield objects
    tcount = 0
    havevars = len(vlist)  # could be zero!
    for ivtxt in vlist:
        textfld = SimpleText(Translation(0, yoff), ivtxt)
        textfld.SetProperties(font=courfont, font_size=pvfnsz)
        textfld.update_rects()
        sbdict['vtf_' + str(tcount)] = textfld
        # add connector line
        tfbr = textfld.bounding_rect
        tfline = getQuickLine((tfbr.left, tfbr.bottom),
                              (tfbr.right + lastcharpad, tfbr.bottom))
        tfline.SetProperties(line_width=2.0,
                             line_arrow1=tarrw1,
                             line_arrow2=tarrw3)
        tfline.update_rects()
        sbdict['vtfl_' + str(tcount)] = tfline
        if tfbr.right + lastcharpad > xoutermost:
            xoutermost = tfbr.right + lastcharpad
        # group line and text
        tgrp = Group([tfline, textfld])
        tgrp.update_rects()
        sbdict['vtfg_' + str(tcount)] = tgrp
        # add the group - not just textfld - to list here
        varbl_tflist.append(tgrp)
        # get height - calc next offset; yoff will go negative, but nvm (will group and move)
        llx, lly, urx, ury = textfld.bounding_rect  # getTfBB(textfld)
        tfHeight = ury - lly
        yoff -= tfHeight + 2
        tcount += 1
    # done - now resize all the arrows according to xoutermost,
    # so they are same length
    # SetLine(i, x, y[, cont]) "Replace the ith segment by a line segment"
    # where the i-th segment is the node..
    for tgrp in varbl_tflist:  # loop (in a list)
        tfline = tgrp.objects[
            0]  # access Group (sub)object - tfline is first [0]
        # get 2nd node (=segment 1) - in there, Point() is third in list ([2])
        tmpep = tfline.paths[0].Segment(1)[2]
        # change 2nd node (=segment 1) of the (first) path (paths[0])
        tfline.paths[0].SetLine(1, (xoutermost, tmpep.y))
        tfline.update_rects()
        tgrp.update_rects()
    # finally, group all these
    varbl_group = Group(varbl_tflist)  # accepts list directly
    varbl_group.update_rects()
    sbdict['vtg'] = varbl_group

    # rotate variable group
    # for repositioning - easiest to rotate around
    #  upper-left corner (instead of center point)
    vgbr = varbl_group.bounding_rect
    varbl_g_cp = vgbr.center()  # centerpoint
    varbl_g_ul = Point(vgbr.left, vgbr.top)  # top (upper) left
    varbl_group.Transform(Rotation(radians(-90), varbl_g_ul))
    varbl_group.update_rects()

    # must reassign variable for .bounding_rect
    #  after transform and update_rects()!!:
    vgbr = varbl_group.bounding_rect
    vgbr_w = vgbr.right - vgbr.left
    vgbr_h = vgbr.top - vgbr.bottom
    # also, below needed for box & move calc:
    #~ point_group.update_rects()
    pgbr = point_group.bounding_rect
    pgbr_w = pgbr.right - pgbr.left
    pgbr_h = pgbr.top - pgbr.bottom

    # and note - groups seem to add some sort of padding, so align to intern lines instead (instead of group BB edges)
    # get first (moveto) Point of first tf's line (align to that, instead of pgbr) - needed for 'boxing' below:
    tfline = point_group[0].objects[0]
    tmpep = tfline.paths[0].Segment(0)[2]
    tmpep_v = Point(0, 0)
    if havevars != 0:
        tmpep_v = varbl_group[0].objects[0].paths[0].Segment(0)[2]
    xmoveadj = tmpep.x - pgbr.left  # adjustment due to group padding
    ymoveadj = -(tmpep_v.y - vgbr.top)  # only for moving varbl_group

    # move rotated varbl_group below left of point_group
    xmove = 0  # -vgbr_w # looks ok like this
    xmove += xmoveadj  # adjust due to group padding
    ymove = -pgbr_h
    ymove += ymoveadj  # adjust due to group padding
    varbl_group.Translate(Point(xmove, ymove))
    varbl_group.update_rects()
    vgbr = varbl_group.bounding_rect

    ## add the box label
    lxpad = 10  # box label x padding
    tfld_label = SimpleText(Translation(0, 0), inlabel)
    tfld_label.SetProperties(font=courfont, font_size=16)
    tfld_label.update_rects()
    # reposition
    # on y: center align tfld_label with center of point_group
    cp_pg = pgbr.center()
    tlbr = tfld_label.bounding_rect
    cp_tl = tlbr.center()
    ymove = -(cp_tl.y - cp_pg.y)
    # on x: <NO>align right edge of tfld_label + pad with left edge of point_group</NO>
    #    center in respect to expected boxwidth instead
    # calc expected boxwidth first
    tlbr = tfld_label.bounding_rect
    tlbr_w = tlbr.right - tlbr.left
    boxwidth = lxpad + tlbr_w + lxpad  # only title text label width + padding
    varbl_width = vgbr.right - vgbr.left
    if boxwidth < lxpad + varbl_width + lxpad:
        boxwidth = lxpad + varbl_width + lxpad
    #~ xmove = -(tlbr.right + lxpad - pgbr.left) # title text left aligned w/ right edge
    xmove = -((tlbr.center().x - pgbr.left) + boxwidth / 2)
    xmove += xmoveadj  # adjust due to group padding
    tfld_label.Translate(Point(xmove, ymove))
    tfld_label.update_rects()
    tlbr = tfld_label.bounding_rect  # must reassign variable (though we won't need it anymore)

    ## create a box for point/varbl_group
    # start at upper left point of point_group BB;
    # go downleft, to <NO>upper left point of varbl_group BB</NO>
    #  .. note - groups seem to add some sort of padding, so align to intern lines:
    # go downleft, to upper edge of varbl_group, and ([left edge of tfld_label] - pad)
    # get first (moveto) Point of first tf's line (align to that, instead of pgbr)
    tfline = point_group[0].objects[0]
    tmpep = tfline.paths[0].Segment(0)[2]
    start_x = tmpep.x  # pgbr.left
    start_y = pgbr.top
    off_x = -(boxwidth)  # -vgbr_w
    off_y = -pgbr_h
    trec = Rectangle(trafo=Trafo(off_x, 0, 0, off_y, start_x, start_y))
    trec.SetProperties(
        line_width=2.0,
        fill_pattern=SolidPattern(CreateRGBColor(0.7, 0.7, 0.9)),
    )
    trec.update_rects()
    sbdict['box'] = trec

    # now group all these
    # grouping goes: 1st on bottom, 2nd on top of it, etc..
    retgrp = Group([trec, tfld_label, point_group, varbl_group])
    retgrp.update_rects()

    # move lower left corner of retgrp to 0,0
    rgbr = retgrp.bounding_rect
    retgrp.Translate(Point(-rgbr.left, -rgbr.bottom))
    retgrp.update_rects()

    return (retgrp, sbdict)