def abut_horizontal(context): # We have to refer to the document frequently, so save some typing # by binding it to the local variable doc. doc = context.document pos = [] for obj in doc.SelectedObjects(): rect = obj.coord_rect pos.append((rect.left, rect.top, rect.right - rect.left, obj)) if pos: # pos is empty (= false) if no object is selected pos.sort() start, top, width, ob = pos[0] next = start + width for left, top, width, obj in pos[1:]: obj.Translate(Point(next - left, 0)) next = next + width
def GetHandles(self): sx = self.start.x sy = self.start.y ex = self.end.x ey = self.end.y x2 = (sx + ex) / 2 y2 = (sy + ey) / 2 return map(handle.MakePixmapHandle, [Point(sx, ey), Point(x2, ey), Point(ex, ey), Point(sx, y2), Point(ex, y2), Point(sx, sy), Point(x2, sy), Point(ex, sy)], [(-1, 1), (0, 1), (1, 1), (-1, 0), (1, 0), (-1, -1), (0, -1), (1, -1)], [pixmaps.TurnTL, pixmaps.ShearLR, pixmaps.TurnTR, pixmaps.ShearUD, pixmaps.ShearUD, pixmaps.TurnBL, pixmaps.ShearLR, pixmaps.TurnBR], [appconst.CurCreate] * 8) \ + [handle.MakePixmapHandle(self.center, (0, 0), pixmaps.Center)]
def constrain_center(self, p, state): if state & const.ConstraintMask: start = self.start end = self.end if p.x < 0.75 * start.x + 0.25 * end.x: x = start.x elif p.x > 0.25 * start.x + 0.75 * end.x: x = end.x else: x = (start.x + end.x) / 2 if p.y < 0.75 * start.y + 0.25 * end.y: y = start.y elif p.y > 0.25 * start.y + 0.75 * end.y: y = end.y else: y = (start.y + end.y) / 2 return Point(x, y) return p
def spread_h_bbox(context): pos = [] sum = 0 for obj in context.document.SelectedObjects(): rect = obj.coord_rect width = rect.right - rect.left pos.append((rect.left, width, obj)) sum = sum + width l = len(pos) - 1 if l > 1: pos.sort() start, width1, ob = pos[0] end, width2, ob = pos[-1] gap = (end + width2 - start - sum) / l next = start + width1 + gap for left, width, obj in pos[1:-1]: obj.Translate(Point(next - left, 0)) next = next + width + gap
def parse_transform(self, trafo_string): trafo = self.trafo trafo_string = as_latin1(trafo_string) while trafo_string: match = rx_trafo.match(trafo_string) if match: function = match.group(1) args = argsf = string.translate(match.group(2), commatospace) args = map(str, split(args)) trafo_string = trafo_string[match.end(0):] if function == 'matrix': args = map(float, split(argsf)) trafo = trafo(apply(Trafo, tuple(args))) elif function == 'scale': if len(args) == 1: sx = sy = args[0] else: sx, sy = args sx, sy = self.user_point(sx, sy) trafo = trafo(Scale(sx, sy)) elif function == 'translate': if len(args) == 1: dx, dy = args[0], '0' else: dx, dy = args dx, dy = self.user_point(dx, dy) trafo = trafo(Translation(dx, dy)) elif function == 'rotate': if len(args) == 1: trafo = trafo(Rotation(float(args[0]) * degrees)) else: angle, cx, cy = args cx, cy = self.user_point(cx, cy) trafo = trafo( Rotation(float(angle) * degrees, Point(cx, cy))) elif function == 'skewX': trafo = trafo( Trafo(1, 0, tan(float(args[0]) * degrees), 1, 0, 0)) elif function == 'skewY': trafo = trafo( Trafo(1, tan(float(args[0]) * degrees), 0, 1, 0, 0)) else: trafo_string = '' self.trafo = trafo
def spread_v_bbox(context): pos = [] sum = 0 for obj in context.document.SelectedObjects(): rect = obj.coord_rect height = rect.top - rect.bottom pos.append((rect.top, height, obj)) sum = sum + height l = len(pos) - 1 if l > 1: pos.sort() pos.reverse() start, height1, ob = pos[0] end, height2, ob = pos[-1] gap = (start - end + height2 - sum) / l next = start - height1 - gap for top, height, obj in pos[1:-1]: obj.Translate(Point(0, next - top)) next = next - height - gap
def __init__(self, foreground = None, background = None, direction = Point(1, 0), spacing = 5.0, width = 0.5, duplicate = None): if duplicate is not None: self.foreground = duplicate.foreground self.background = duplicate.background self.spacing = duplicate.spacing self.width = duplicate.width self.direction = duplicate.direction elif foreground: self.foreground = foreground if not background: background = color.StandardColors.white self.background = background self.spacing = spacing self.width = width self.direction = direction else: raise ValueError,\ 'HatchingPattern must be created with color argument'
def subdivide_curve(p0, p1, p2, p3, threshold=1.0, r=(0.0, 1.0)): buffer = [] p10 = Point(subdivide(p0.x, p1.x), subdivide(p0.y, p1.y)) p11 = Point(subdivide(p1.x, p2.x), subdivide(p1.y, p2.y)) p12 = Point(subdivide(p2.x, p3.x), subdivide(p2.y, p3.y)) p20 = Point(subdivide(p10.x, p11.x), subdivide(p10.y, p11.y)) p21 = Point(subdivide(p11.x, p12.x), subdivide(p11.y, p12.y)) p30 = Point(subdivide(p20.x, p21.x), subdivide(p20.y, p21.y)) t = subdivide(r[0], r[1]) if math.hypot(p0.x - p30.x, p0.y - p30.y) > threshold: buffer.extend(subdivide_curve(p0, p10, p20, p30, threshold, (r[0], t))) buffer.append((p30, t)) if math.hypot(p30.x - p3.x, p30.y - p3.y) > threshold: buffer.extend(subdivide_curve(p30, p21, p12, p3, threshold, (t, r[1]))) return buffer
def create_spiral_path(rotation, radius): r = unit.convert(radius) rate = r / (rotation * 2 * pi) def tangent(phi, a=0.55197 * rate): return a * Point(cos(phi) - phi * sin(phi), sin(phi) + phi * cos(phi)) pi2 = pi / 2.0 angle = 0 tang = tangent(0) path = CreatePath() p = Point(0, 0) path.AppendLine(p) for i in range(rotation * 4): p1 = p + tang angle = pi2 * (i + 1) p = Polar(rate * angle, angle) tang = tangent(angle) p2 = p - tang path.AppendBezier(p1, p2, p, ContSymmetrical) return path
def ButtonDown(self, p, button, state): SelectAndDrag.DragStart(self, p) sel = self.selection if sel == -1: if self.anchor: #XXX shouldn't this be 'if self.anchor is not None' start = self.anchor else: start = self.start self.drag_start = self.drag_cur = start return (p - start, self.coord_rect.translated(-start)) ds_x, ds_y = (self.start + self.end) / 2 if sel in self.selLeft: ds_x = self.start.x if sel in self.selTop: ds_y = self.start.y if sel in self.selRight: ds_x = self.end.x if sel in self.selBottom: ds_y = self.end.y self.drag_cur = self.drag_start = ds = Point(ds_x, ds_y) self.init_constraint() return p - ds
def Ellipse(self, ell): trf = ell.trafo if (trf.m12 == 0 and trf.m21 == 0) or (trf.m11 == 0 and trf.m22 == 0): self.FillStyle(ell.Properties()) left, top, right, bottom = self.rect_to_ltrb(ell, Point(-1, -1)) if ell.start_angle == ell.end_angle: self.packrec('<LHhhhh', 7, 0x0418, bottom, right, top, left) else: xe, ye = map(rndtoint, self.trafo(ell.trafo(Polar(1, ell.start_angle)))) xs, ys = map(rndtoint, self.trafo(ell.trafo(Polar(1, ell.end_angle)))) if ell.arc_type == const.ArcArc: function = 0x0817 elif ell.arc_type == const.ArcPieSlice: function = 0x081A elif ell.arc_type == const.ArcChord: function = 0x0830 self.packrec('<LHhhhhhhhh', 11, function, ye, xe, ys, xs, bottom, right, top, left) else: self.PolyBezier(ell.Paths(), ell.Properties())
def apply_move(self, *arg): if self.button["state"] == DISABLED: return try: var_x = self.var_width.get() var_y = self.var_height.get() except: return x, y = self.coordinates(self.var_position.get()) if self.var_position.get() == RELATIVE: if self.var_width_base != self.var_width.get( ) or self.var_height_base != self.var_height.get(): self.var_basepoint.set('USER') x, y = var_x, var_y else: x, y = var_x - x, var_y - y if arg and arg[0] == 'Duplicate': self.document.MoveAndCopy(x, y, Point(0, 0)) else: self.document.MoveSelected(x, y)
def update_rects(self): trafo = self.trafo start = trafo.offset() # On some systems, atan2 can raise a ValueError if both # parameters are 0. In that case, the actual value the of angle # is not important since in the computation of p below, the # coordinate depending on the angle will always be 0 because # both trafo coefficients are 0. So set the angle to 0 in case # of an exception. try: phi1 = atan2(trafo.m12, trafo.m11) except ValueError: phi1 = 0 try: phi2 = atan2(trafo.m22, trafo.m21) except ValueError: phi2 = 0 p = Point(trafo.m11 * cos(phi1) + trafo.m12 * sin(phi1), trafo.m21 * cos(phi2) + trafo.m22 * sin(phi2)) self.coord_rect = r = Rect(start + p, start - p) if self.properties.HasLine(): width = self.properties.line_width r = r.grown(width / 2 + 1) # add the bounding boxes of arrows if self.arc_type == ArcArc: pi2 = pi / 2 arrow1 = self.properties.line_arrow1 if arrow1 is not None: pos = trafo(Polar(1, self.start_angle)) dir = trafo.DTransform(Polar(1, self.start_angle - pi2)) r = UnionRects(r, arrow1.BoundingRect(pos, dir, width)) arrow2 = self.properties.line_arrow2 if arrow2 is not None: pos = trafo(Polar(1, self.end_angle)) dir = trafo.DTransform(Polar(1, self.end_angle + pi2)) r = UnionRects(r, arrow2.BoundingRect(pos, dir, width)) self.bounding_rect = r
def user_point(self, x, y): # Return the point described by the SVG coordinates x and y as # an SKPoint object in user coordinates. x and y are expected to # be strings. x = strip(x) y = strip(y) # extract the units from the coordinate values if any and # determine the appropriate factor to convert those units to # user space units. xunit = x[-2:] factor = factors.get(xunit) if factor is not None: x = x[:-2] elif x[-1] == '%': x = x[:-1] xunit = '%' factor = self.viewPort[2] / 100.0 else: xunit = '' factor = 1.0 x = float(x) * factor yunit = y[-2:] factor = factors.get(yunit) if factor is not None: y = y[:-2] elif y[-1] == '%': y = y[:-1] yunit = '%' factor = self.viewPort[3] / 100.0 else: yunit = '' factor = 1.0 y = float(y) * factor return Point(x, y)
def Transform(self, trafo, rects=None): if rects: r1, r2 = rects left, bottom, right, top = r1 cx, cy = self.center cx = cx * right + (1 - cx) * left cy = cy * top + (1 - cy) * bottom cx, cy = trafo(cx, cy) left, bottom, right, top = r2 len = right - left if len: cx = (cx - left) / len else: cx = 0 len = top - bottom if len: cy = (cy - bottom) / len else: cy = 0 center = Point(cx, cy) else: center = self.center return self.SetCenter(center)
def convert_paths(self, paths_list): paths = () for path in paths_list: p = CreatePath() p.AppendLine(Point(*path[0])) points = path[1] for point in points: if len(point) == 2: p.AppendLine(Point(*point)) else: point0 = Point(*point[0]) point1 = Point(*point[1]) point2 = Point(*point[2]) p.AppendBezier(point0, point1, point2, point[3]) if path[2]: p.AppendLine(Point(*path[0])) p.ClosePath() paths = paths + (p,) return paths
def parse_path(self, str): paths = self.paths path = self.path trafo = self.trafo str = strip(string.translate(as_latin1(str), commatospace)) last_quad = None last_cmd = cmd = None f13 = 1.0 / 3.0; f23 = 2.0 / 3.0 #print '*', str while 1: match = rx_command.match(str) #print match if match: last_cmd = cmd cmd = str[0] str = str[match.end():] #print '*', str points = match.group(1) #print '**', points if points: # use tokenize_line to parse the arguments so that # we deal with signed numbers following another # number without intervening whitespace other # characters properls. # FIXME: tokenize_line works but is not the best way # to do it because it accepts input that wouldn't be # valid here. points = filter(operator.isNumberType, skread.tokenize_line(points)) #print cmd, points if cmd in 'mM': path = CreatePath() paths.append(path) if cmd == 'M' or len(paths) == 1: path.AppendLine(trafo(points[0], points[1])) else: p = trafo.DTransform(points[0], points[1]) path.AppendLine(paths[-2].Node(-1) + p) if len(points) > 2: if cmd == 'm': for i in range(2, len(points), 2): p = trafo.DTransform(points[i], points[i + 1]) path.AppendLine(path.Node(-1) + p) else: for i in range(2, len(points), 2): path.AppendLine(trafo(points[i], points[i+1])) elif cmd == 'l': for i in range(0, len(points), 2): p = trafo.DTransform(points[i], points[i + 1]) path.AppendLine(path.Node(-1) + p) elif cmd == 'L': for i in range(0, len(points), 2): path.AppendLine(trafo(points[i], points[i+1])) elif cmd =='H': for num in points: path.AppendLine(Point(num, path.Node(-1).y)) elif cmd =='h': for num in points: x, y = path.Node(-1) dx, dy = trafo.DTransform(num, 0) path.AppendLine(Point(x + dx, y + dy)) elif cmd =='V': for num in points: path.AppendLine(Point(path.Node(-1).x, num)) elif cmd =='v': for num in points: x, y = path.Node(-1) dx, dy = trafo.DTransform(0, num) path.AppendLine(Point(x + dx, y + dy)) elif cmd == 'C': if len(points) % 6 != 0: self.loader.add_message("number of parameters of 'C'"\ "must be multiple of 6") else: for i in range(0, len(points), 6): p1 = trafo(points[i], points[i + 1]) p2 = trafo(points[i + 2], points[i + 3]) p3 = trafo(points[i + 4], points[i + 5]) path.AppendBezier(p1, p2, p3) elif cmd == 'c': if len(points) % 6 != 0: self.loader.add_message("number of parameters of 'c'"\ "must be multiple of 6") else: for i in range(0, len(points), 6): p = path.Node(-1) p1 = p + trafo.DTransform(points[i], points[i + 1]) p2 = p + trafo.DTransform(points[i+2], points[i+3]) p3 = p + trafo.DTransform(points[i+4], points[i+5]) path.AppendBezier(p1, p2, p3) elif cmd == 'S': if len(points) % 4 != 0: self.loader.add_message("number of parameters of 'S'"\ "must be multiple of 4") else: for i in range(0, len(points), 4): type, controls, p, cont = path.Segment(-1) if type == Bezier: q = controls[1] else: q = p p1 = 2 * p - q p2 = trafo(points[i], points[i + 1]) p3 = trafo(points[i + 2], points[i + 3]) path.AppendBezier(p1, p2, p3) elif cmd == 's': if len(points) % 4 != 0: self.loader.add_message("number of parameters of 's'"\ "must be multiple of 4") else: for i in range(0, len(points), 4): type, controls, p, cont = path.Segment(-1) if type == Bezier: q = controls[1] else: q = p p1 = 2 * p - q p2 = p + trafo.DTransform(points[i], points[i + 1]) p3 = p + trafo.DTransform(points[i+2], points[i+3]) path.AppendBezier(p1, p2, p3) elif cmd == 'Q': if len(points) % 4 != 0: self.loader.add_message("number of parameters of 'Q'"\ "must be multiple of 4") else: for i in range(0, len(points), 4): q = trafo(points[i], points[i + 1]) p3 = trafo(points[i + 2], points[i + 3]) p1 = f13 * path.Node(-1) + f23 * q p2 = f13 * p3 + f23 * q path.AppendBezier(p1, p2, p3) last_quad = q elif cmd == 'q': if len(points) % 4 != 0: self.loader.add_message("number of parameters of 'q'"\ "must be multiple of 4") else: for i in range(0, len(points), 4): p = path.Node(-1) q = p + trafo.DTransform(points[i], points[i + 1]) p3 = p + trafo.DTransform(points[i+2], points[i+3]) p1 = f13 * p + f23 * q p2 = f13 * p3 + f23 * q path.AppendBezier(p1, p2, p3) last_quad = q elif cmd == 'T': if len(points) % 2 != 0: self.loader.add_message("number of parameters of 'T'"\ "must be multiple of 4") else: if last_cmd not in 'QqTt' or last_quad is None: last_quad = path.Node(-1) for i in range(0, len(points), 2): p = path.Node(-1) q = 2 * p - last_quad p3 = trafo(points[i], points[i + 1]) p1 = f13 * p + f23 * q p2 = f13 * p3 + f23 * q path.AppendBezier(p1, p2, p3) last_quad = q elif cmd == 't': if len(points) % 2 != 0: self.loader.add_message("number of parameters of 't'"\ "must be multiple of 4") else: if last_cmd not in 'QqTt' or last_quad is None: last_quad = path.Node(-1) for i in range(0, len(points), 2): p = path.Node(-1) q = 2 * p - last_quad p3 = p + trafo.DTransform(points[i], points[i + 1]) p1 = f13 * p + f23 * q p2 = f13 * p3 + f23 * q path.AppendBezier(p1, p2, p3) last_quad = q elif cmd in 'zZ': if round(path.Node(0).x, 3) != round(path.Node(-1).x, 3) or \ round(path.Node(0).y, 3) != round(path.Node(-1).y, 3): path.AppendLine(path.Node(0)) path.ClosePath() else: break self.path = path
def try_add_style(self,key,val): if key == 'fill': if val == 'none': self.style.fill_pattern = EmptyPattern elif val[:3] == 'url' and self.grad_patters.has_key(val[5:-1]): grad=self.grad_patters[val[5:-1]] try: if grad[0]=='LinearGradient': point1,point2=grad[2] point1=self.trafo(point1) point2=self.trafo(point2) point=Point(point2.x-point1.x, point2.y-point1.y) if not grad[1].__class__ == MultiGradient: if self.gradients.has_key(grad[1]): self.style.fill_pattern = LinearGradient(self.gradients[grad[1]].Duplicate(),point) else: self.style.fill_pattern = LinearGradient(grad[1].Duplicate(),point) if grad[0]=='RadialGradient': point1,point2=grad[2] point1=self.trafo(point1) point2=self.trafo(point2) point1=Point(0.5,0.5) if not grad[1].__class__ == MultiGradient: if self.gradients.has_key(grad[1]): self.style.fill_pattern = RadialGradient(self.gradients[grad[1]].Duplicate(),point1) else: self.style.fill_pattern = RadialGradient(grad[1].Duplicate(),point1) except: pass else: color = csscolor(val) self._print('fill', color) self.style.fill_pattern = SolidPattern(color) elif key == 'fill-opacity': value=atof(val) if self.style.fill_pattern.__class__ == SolidPattern: self.style.fill_pattern.Color().alpha*=value self.style.fill_pattern.Color().update() 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) if not self.opacity is None: self.style.line_pattern.Color().alpha=self.opacity self.style.line_pattern.Color().update() elif key == 'stroke-opacity': value=atof(val) if self.style.line_pattern.__class__ == SolidPattern: self.style.line_pattern.Color().alpha*=value self.style.line_pattern.Color().update() elif key == 'opacity': value=atof(val) self.opacity=value if self.style.fill_pattern.__class__ == SolidPattern: self.style.fill_pattern.Color().alpha=value self.style.fill_pattern.Color().update() if self.style.line_pattern.__class__ == SolidPattern: self.style.line_pattern.Color().alpha=self.opacity self.style.line_pattern.Color().update() 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 = abs(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': self.style.font = GetFont(val) elif key == '-inkscape-font-specification': self.style.font = GetFont(val) 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
def average_points(context): # find a bezier polygon selected selection = [] for object in context.document.SelectedObjects(): if not object.is_Bezier: continue selection.append(object) if len(selection) != 1: context.application.MessageBox(title="Average Points", message="Select one polygon.") return None # count selected points object = selection[0] object_paths = object.Paths() npoints = 0 for path in object_paths: for i in range(path.len): if path.SegmentSelected(i): npoints = npoints + 1 if npoints == 0: context.application.MessageBox(title="Average Points", message="Select two or more points.") return None # inquiry parameters which = AverageDialog(context.application.root).RunDialog() if which is None: return None # compute average coordinates of the selected points ax = 0 ay = 0 modified_paths = [] for path in object_paths: modified_paths.append([]) for i in range(path.len): type, controls, point, cont = path.Segment(i) modified_paths[-1].append([type, list(controls), point, cont]) if path.SegmentSelected(i): ax = ax + point.x ay = ay + point.y ax = float(ax) / npoints ay = float(ay) / npoints # translate the selected points for i in range(len(object_paths)): path = object_paths[i] new_path = modified_paths[i] for j in range(path.len): if path.SegmentSelected(j): point = new_path[j][2] if which == AVERAGE_X: new_point = Point(ax, point.y) elif which == AVERAGE_Y: new_point = Point(point.x, ay) else: new_point = Point(ax, ay) new_path[j][2] = new_point offset = point - new_point if len(new_path[j][1]) == 2: new_path[j][1][1] = new_path[j][1][1] - offset if j < path.len - 1 and len(new_path[j + 1][1]) == 2: new_path[j + 1][1][0] = new_path[j + 1][1][0] - offset # create new paths new_paths = [] for i in range(len(object_paths)): path = object_paths[i] new_path = CreatePath() for type, controls, point, cont in modified_paths[i]: new_path.AppendSegment(type, tuple(controls), point, cont) if path.closed: new_path.AppendLine(new_path.Node(0)) new_path.ClosePath() new_paths.append(new_path) # set the new paths undo = object.SetPaths(new_paths) # return Undo info return undo
def SetLowerLeftCorner(self, corner): # move self so that self's lower left corner is at CORNER. This # used when interactively placing an object rect = self.coord_rect ll = Point(rect.left, rect.bottom) return self.Translate(corner - ll)
def LayoutPoint(self): return Point(self.coord_rect.left, self.coord_rect.bottom)
def setcurrentpoint(self): x, y = self.pop_all() self.cur = Point(x, y)
def vmoveto(self): dy = self.pop_all() self.cur = self.cur + Point(0, dy) self.new_path() self.path.append(tuple(self.cur))
def hmoveto(self): dx = self.pop_all() self.cur = self.cur + Point(dx, 0) self.new_path() self.path.append(tuple(self.cur))
def phs(self, color, background, dx, dy, dist, width): self.pattern = HatchingPattern(self.convert_color(color), self.convert_color(background), Point(dx, dy), dist, width)
def pgr(self, dx, dy, border=0): if not self.gradient: raise SketchLoadError(_("No gradient for gradient pattern")) self.pattern = RadialGradient(self.gradient, Point(dx, dy), border)
def pgc(self, cx, cy, dx, dy): if not self.gradient: raise SketchLoadError(_("No gradient for gradient pattern")) self.pattern = ConicalGradient(self.gradient, Point(cx, cy), Point(dx, dy))
def hlineto(self): dx = self.pop_all() self.cur = self.cur + Point(dx, 0) self.path.append(tuple(self.cur))
def guide(self, pos, horizontal): if horizontal: p = Point(0, pos) else: p = Point(pos, 0) self.append_object(GuideLine(p, horizontal))
def vlineto(self): dy = self.pop_all() self.cur = self.cur + Point(0, dy) self.path.append(tuple(self.cur))