def _calc_shape_path(self, node): E = self._eval_func(node) style = E('roundingStyle') if style == 'screen': r = E('roundness') r = min(max(0, r / 2.), 1) return _ctx.rect(0, 0, node.width, node.height, roundness=r, draw=False) elif style == 'arc': r = E('cornerRadius') r = min(r, node.height / 2.) r = min(r, node.width / 2.) points = geom.rounded_rect(0, 0, node.width, node.height, r) return createpath(_ctx, points, close=False)
def _calc_shape_path(self, node): return createpath(_ctx, node._shape_points)
def draw(self, node): """ Draw the node at its (x,y) anchor point. Relies on internal properties precalculated by precalc_node. """ E = self._eval_func(node) strokewidth = E('strokeWidth') drawstroke = strokewidth > 0 d = node._boxdepth _ctx.push() _ctx.translate(node.x, node.y) if drawstroke: # Set up clip path cx1 = cx6 = 0 cy2 = 0 cx3 = cx4 = cx1 + node.width + d cy5 = cy2 + node.height + d if self._vert_dir == self._horiz_dir: cy1 = d cx2 = d cy3 = 0 cy4 = cy3 + node.height cx5 = node.width cy6 = cy4 + d elif self._vert_dir != self._horiz_dir: cy1 = 0 cx2 = node.width cy3 = d cy4 = cy3 + node.height cx5 = d cy6 = cy4 - d outline = [ Vector2(cx1, cy1), Vector2(cx2, cy2), Vector2(cx3, cy3), Vector2(cx4, cy4), Vector2(cx5, cy5), Vector2(cx6, cy6) ] offs = geom.offset_poly(outline, strokewidth * .5) clippath = createpath(_ctx, offs) _ctx.beginclip(clippath) # Box drawing stuff if drawstroke: _ctx.stroke(E('strokeColor')) _ctx.strokewidth(strokewidth) else: _ctx.nostroke() if self._vert_dir == 1: y1 = d y2 = y1 - d dv = -d oy = y1 else: y1 = node.height y2 = y1 + d dv = d oy = 0 if self._horiz_dir == 1: x1 = node.width x2 = x1 + d dh = d ox = 0 else: x1 = d x2 = x1 - d dh = -d ox = x1 # Draw box path = _ctx.rect(ox, oy, node.width, node.height, draw=False) self._draw_gradient_shape(node, path, node.fillcolor) # Draw horizontal 3D side _ctx.beginpath(ox, y1) _ctx.lineto(ox + node.width, y1) _ctx.lineto(ox + node.width + dh, y2) _ctx.lineto(ox + dh, y2) _ctx.lineto(ox, y1) path = _ctx.endpath(draw=False) col = E('horizSideColor') self._draw_gradient_shape(node, path, col) # Draw vertical 3D side _ctx.beginpath(x1, oy) _ctx.lineto(x1, oy + node.height) _ctx.lineto(x2, oy + node.height + dv) _ctx.lineto(x2, oy + dv) _ctx.lineto(x1, oy) path = _ctx.endpath(draw=False) col = E('vertSideColor') self._draw_gradient_shape(node, path, col) tx = node._textxoffs + ox ty = node._textyoffs + oy _ctx.fill(node.fontcolor) self._drawtext(node, tx, ty) if drawstroke: _ctx.endclip() _ctx.pop()
def _draw(self, node, direction=None): """ Draw a curved connection between a node and its child nodes. """ E = self._eval_func(node) children = node.getchildren(direction) if not children: return linewidth = E('lineWidth') _ctx.autoclosepath(True) _ctx.stroke(node.connectioncolor) _ctx.fill(node.connectioncolor) _ctx.strokewidth(linewidth) firstchild = children[0] lastchild = children[-1] direction = firstchild.direction() opp_direction = opposite_dir(direction) x1, y1 = node.connection_point(direction) xfirst, yfirst = firstchild.connection_point(opp_direction) # Special case: draw straight line if there's only one child if len(children) == 1: _ctx.line(x1, y1, xfirst, yfirst) return # Calculate junction point position jx = x1 + (xfirst - x1) * E('junctionXFactor') jy = y1 # Draw line from parent node to junction point _ctx.line(x1, y1, jx, jy) # Limit first & last corner radius to the available area ylast = lastchild.connection_point(opp_direction)[1] ysecond = children[1].connection_point(opp_direction)[1] ypenultimate = children[-2].connection_point(opp_direction)[1] # Starting corner radius cornerPad = E('cornerPad') r = min(E('cornerRadius'), abs(jx - xfirst) - cornerPad) r = max(r, 0) # Adjusted first (top) corner radius r1 = min(r, abs(yfirst - jy) - cornerPad) r1 = max(r1, 0) if ysecond < jy: r1 = min(r, abs(yfirst - ysecond) - cornerPad) r1 = max(r1, 0) # Adjusted last (bottom) corner radius r2 = min(r, abs(ylast - jy) - cornerPad) r2 = max(r2, 0) if ypenultimate > jy: r2 = min(r, abs(ylast - ypenultimate) - cornerPad) r2 = max(r2, 0) # Draw main branch as a single path to ensure line continuity p1 = Vector2(jx, yfirst + r1) p2 = Vector2(jx, ylast - r2) segments = [[p1, p2]] corner_style = E('cornerStyle') for i, child in enumerate(children): direction = child.direction() opp_direction = opposite_dir(direction) x2, y2 = child.connection_point(opp_direction) if direction == Direction.Left: x2 -= linewidth / 2 elif direction == Direction.Right: x2 += linewidth / 2 # Draw corners if direction == Direction.Left: a1 = 90 da = -90 dx1 = r1 * 2 dx2 = r2 * 2 else: a1 = da = 90 dx1 = dx2 = 0 x1 = jx if child is firstchild: x1 += -r1 if direction == Direction.Left else r1 if (corner_style == 'square' or abs(y2 - jy) < .001): p1 = Vector2(jx, y2) p2 = Vector2(jx, y2 + r1) segments.insert(0, [p1, p2]) p1 = Vector2(x1, y2) p2 = Vector2(jx, y2) segments.insert(0, [p1, p2]) elif corner_style == 'beveled': p1 = Vector2(x1, y2) p2 = Vector2(jx, y2 + r1) segments.insert(0, [p1, p2]) elif corner_style == 'rounded': arc = arcpath(jx - dx1, y2, r1 * 2, r1 * 2, a1, da) segments = arc + segments p1 = Vector2(x2, y2) p2 = Vector2(x1, y2) segments.insert(0, [p1, p2]) elif child is lastchild: x1 += -r2 if direction == Direction.Left else r2 if (corner_style == 'square' or abs(y2 - jy) < .001): p1 = Vector2(jx, y2 - r2) p2 = Vector2(jx, y2) segments.append([p1, p2]) p1 = Vector2(jx, y2) p2 = Vector2(x1, y2) segments.append([p1, p2]) elif corner_style == 'beveled': p1 = Vector2(jx, y2 - r2) p2 = Vector2(x1, y2) segments.append([p1, p2]) elif corner_style == 'rounded': arc = arcpath(jx - dx2, y2 - r2 * 2, r2 * 2, r2 * 2, a1 + da, da) segments = segments + arc p1 = Vector2(x1, y2) p2 = Vector2(x2, y2) segments.append([p1, p2]) else: _ctx.line(x1, y2, x2, y2) # Draw main branch path _ctx.nofill() path = createpath(_ctx, segments, close=False) _ctx.drawpath(path) # Draw junction point style = E('junctionStyle') if style == 'none': return r = E('junctionRadius') r2 = r / 2. _ctx.fill(E('junctionFillColor')) _ctx.stroke(E('junctionStrokeColor')) _ctx.strokewidth(E('junctionStrokeWidth')) if style == 'square': _ctx.rect(jx - r2, jy - r2, r, r) elif style == 'disc': _ctx.oval(jx - r2, jy - r2, r, r) elif style == 'diamond': _ctx.beginpath(jx, jy - r2) _ctx.lineto(jx + r2, jy) _ctx.lineto(jx, jy + r2) _ctx.lineto(jx - r2, jy) _ctx.lineto(jx, jy - r2) _ctx.endpath() # Draw junction sign sign = E('junctionSign') if sign == 'none': return _ctx.stroke(E('junctionSignColor')) d = E('junctionSignSize') / 2. _ctx.strokewidth(E('junctionSignStrokeWidth')) if sign in ('minus', 'plus'): _ctx.line(jx - d, jy, jx + d, jy) if sign == 'plus': _ctx.line(jx, jy - d, jx, jy + d)