def Snap(self, p): try: r, phi = self.trafo.inverse()(p).polar() start_angle = self.start_angle; end_angle = self.end_angle p2 = self.trafo(Polar(1, phi)) if start_angle == end_angle: result = (abs(p - p2), p2) else: result = [] if phi < 0: phi = phi + 2 * pi if start_angle < end_angle: between = start_angle <= phi <= end_angle else: between = start_angle <= phi or phi <= end_angle if between: result.append((abs(p - p2), p2)) start = self.trafo(Polar(self.start_angle)) end = self.trafo(Polar(self.end_angle)) if self.arc_type == ArcArc: result.append((abs(start - p), start)) result.append((abs(end - p), end)) elif self.arc_type == ArcChord: result.append((snap_to_line(start, end, p))) elif self.arc_type == ArcPieSlice: center = self.trafo.offset() result.append(snap_to_line(start, center, p)) result.append(snap_to_line(end, center, p)) result = min(result) return result except SingularMatrix: # XXX this case could be handled better. return (1e200, p)
def create_star_path(corners, outer_radius, inner_radius): outer_radius = unit.convert(outer_radius) inner_radius = unit.convert(inner_radius) path = CreatePath() angle = math.pi * 2 / corners for i in range(corners): path.AppendLine(Polar(outer_radius, angle * i)) path.AppendLine(Polar(inner_radius, angle * i + angle / 2)) path.AppendLine(path.Node(0)) path.ClosePath() return path
def gradient_geometry(self, flag, name, xorig, yorig, angle, length, a, b, c, d, tx, ty): trafo = Trafo(a, b, c, d, tx, ty) trafo = artboard_trafo_inv(trafo(artboard_trafo)) start = Point(xorig, yorig) end = start + Polar(length, (pi * angle) / 180.0) self.gradient_geo = (name, trafo, start, end)
def apply_constraint(self, p, state): if state & const.ConstraintMask: r, phi = (p - self.start).polar() pi12 = pi / 12 phi = pi12 * floor(phi / pi12 + 0.5) p = self.start + Polar(r, phi) return p
def recompute(self): h1, h2, h3, theta, p1, p2 = self.h1, self.h2, self.h3, self.theta, \ self.p1, self.p2 xvect = (p2 - p1).normalized() angle = (p2 - p1).polar()[1] + theta * pi / 180 yvect = Polar(1, angle) new = Sketch.CreatePath() newpaths = [new] new.AppendLine(p1) new.AppendLine(p1 + h1 * yvect) new = Sketch.CreatePath() newpaths.append(new) new.AppendLine(p1 + h3 * yvect) new.AppendLine(p2 + h3 * yvect) new = Sketch.CreatePath() newpaths.append(new) new.AppendLine(p2) new.AppendLine(p2 + h2 * yvect) for new in newpaths: new.Transform(self.trafo) if self.objects: self.objects[0].SetPaths(newpaths) else: lines = Sketch.PolyBezier(tuple(newpaths)) self.set_objects([lines])
def apply_constraint(self, p, state): if state & const.ConstraintMask: if self.selection in self.selAspect: ref_x, ref_y = self.reference aspect = self.aspect if aspect is None: # width is 0 p = Point(self.drag_start.x, p.y) else: w = p.x - ref_x h = p.y - ref_y if w == 0: w = 0.00001 a = h / w if a > 0: sign = 1 else: sign = -1 if abs(a) > aspect: h = sign * w * aspect else: w = sign * h / aspect p = Point(ref_x + w, ref_y + h) elif self.selection == -1: pi4 = math.pi / 4 off = p - self.drag_start d = Polar(pi4 * round(math.atan2(off.y, off.x) / pi4)) p = self.drag_start + (off * d) * d return p
def GetHandles(self): handles = [] r1 = self.r1 r2 = self.r2 theta1 = self.theta1 theta2 = self.theta2 p1 = Polar(r1, theta1*pi/180) p2 = Polar(r2, theta1*pi/180) p3 = Polar(r1, theta2*pi/180) p4 = Polar(r2, theta2*pi/180) p5 = 0.5*(p1+p2) p6 = 0.5*(p3+p4) for p in (p1, p2, p3, p4, p5, p6): handles.append(handle.MakeControlHandle(self.trafo(p))) return handles
def GetHandles(self): handles = [] theta1 = self.theta1 theta2 = self.theta2 if theta2 < theta1: theta2 = theta2 + 360 p1 = Polar(self.l1, theta1 * pi / 180) p2 = Polar(self.l2, theta2 * pi / 180) p3 = Rotation(pi / 360 * (theta2 - theta1))(Polar( self.r, theta1 * pi / 180)) for p in (p1, p2): handles.append(handle.MakeNodeHandle(self.trafo(p))) for p in (0.5 * p1, 0.5 * p2, p3): handles.append(handle.MakeControlHandle(self.trafo(p))) return handles
def GetSnapPoints(self): t = self.trafo start_angle = self.start_angle; end_angle = self.end_angle if self.start_angle == self.end_angle: a = Point(t.m11, t.m21) b = Point(t.m12, t.m22) c = t.offset() return [c, c + a, c - a, c + b, c - b] else: points = [t(Polar(start_angle)), t(Polar(end_angle)), t.offset()] if end_angle < start_angle: end_angle = end_angle + 2 * pi pi2 = pi / 2 angle = pi2 * (floor(start_angle / pi2) + 1) while angle < end_angle: points.append(t(Polar(1, angle))) angle = angle + pi2 return points
def Snap(self, p): try: x, y = self.trafo.inverse()(p) minx = self.radius1 maxx = 1 - self.radius1 miny = self.radius2 maxy = 1 - self.radius2 if minx < x < maxx: if miny < y < maxy: ratio = hypot(self.trafo.m11, self.trafo.m21) \ / hypot(self.trafo.m12, self.trafo.m22) if x < 0.5: dx = x else: dx = 1 - x if y < 0.5: dy = y else: dy = 1 - y if dy / dx > ratio: x = round(x) else: y = round(y) elif y > maxy: y = 1 else: y = 0 elif miny < y < maxy: if x > maxx: x = 1 else: x = 0 elif minx > 0 and miny > 0: # the round corners if x < 0.5: cx = minx else: cx = maxx if y < 0.5: cy = miny else: cy = maxy trafo = Trafo(minx, 0, 0, miny, cx, cy) r, phi = trafo.inverse()(x, y).polar() x, y = trafo(Polar(1, phi)) else: # normal corners x = round(min(max(x, 0), 1)) y = round(min(max(y, 0), 1)) p2 = self.trafo(x, y) return (abs(p - p2), p2) except SingularMatrix: return (1e200, p)
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 recompute(self): r1 = self.r1 r2 = self.r2 theta1 = self.theta1 theta2 = self.theta2 if theta2 < theta1: theta2 = theta2+360 ring2 = Sketch.Ellipse(start_angle=theta1*pi/180, end_angle=theta2*pi/180, arc_type=0).Paths()[0] ring1 = ring2.Duplicate() ring2.Transform(Scale(r2)) ring1.Transform(Scale(r1)) new = Sketch.CreatePath() newpaths = [new] new.AppendLine(Polar(r1, theta1*pi/180.)) for i in range(ring2.len): segment = ring2.Segment(i) new.AppendSegment(*segment) new.AppendLine(Polar(r2, theta2*pi/180.)) new.AppendLine(Polar(r1, theta2*pi/180.)) ring1.Transform(Scale(-1,1)) ring1.Transform(Rotation((180+theta1+theta2)*pi/180.)) for i in range(ring1.len): s = ring1.Segment(i) new.AppendSegment(*s) for path in newpaths: path.Transform(self.trafo) if self.objects: self.objects[0].SetPaths(newpaths) else: obj = Sketch.PolyBezier(tuple(newpaths)) self.set_objects([obj])
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 DragHandle(self, pa, pb, selection): try: inverse = self.trafo.inverse() except SingularMatrix: return pa, pb = map(inverse, (pa, pb)) delta = pb - pa theta1 = self.theta1 theta2 = self.theta2 if theta2 < theta1: theta2 = theta2 + 360 p1 = Polar(self.l1, theta1 * pi / 180) p2 = Polar(self.l2, theta2 * pi / 180) p3 = Rotation(pi / 360 * (theta2 - theta1))(Polar( self.r, theta1 * pi / 180)) if selection == 1: p1 = p1 + delta self.l1, angle = p1.polar() if not self.shift_pressed: self.theta1 = angle * 180 / pi elif selection == 2: p2 = p2 + delta self.l2, angle = p2.polar() if not self.shift_pressed: self.theta2 = angle * 180 / pi elif selection == 3: self.theta1 = (0.5 * p1 + delta).polar()[1] * 180 / pi elif selection == 4: self.theta2 = (0.5 * p2 + delta).polar()[1] * 180 / pi elif selection == 5: p3 = p3 + delta l, angle = p3.polar() angle = angle * 180 / pi % 360 begin = self.theta1 % 360 end = self.theta2 % 360 if end < begin: end = end + 360 if angle >= begin and angle <= end: self.r = l else: self.r = 0 if self.control_pressed: if selection in (1, 3): self.theta1 = int(0.5 + self.theta1 / 5.) * 5 elif selection in (2, 4): self.theta2 = int(0.5 + self.theta2 / 5.) * 5 self.recompute()
def recompute(self): path = CreatePath() vertices = self.vertices radius = self.radius twopi = 2 * pi halfpi = pi / 2 for i in range(vertices + 1): path.AppendLine(Polar(radius, (twopi * i) / vertices + halfpi), ContAngle) path.ClosePath() path.Transform(self.trafo) if self.objects: self.objects[0].SetPaths((path,)) else: self.set_objects([PolyBezier((path,))])
def DragHandle(self, pa, pb, selection): try: inverse = self.trafo.inverse() except SingulareMatrix: return pa, pb = map(inverse, (pa, pb)) delta = pb - pa p1, p2, p3 = self.p1, self.p2, self.p3 if self.control_pressed: c = self.Calc()[0] l1 = (p1 - c).polar()[0] l2 = (p2 - c).polar()[0] l3 = (p3 - c).polar()[0] if selection == 1 or self.shift_pressed: p1 = p1 + delta if self.control_pressed: angle = (p1 - c).polar()[1] p1 = Polar(l1, angle) + c self.p1 = p1 if selection == 2 or self.shift_pressed: p2 = p2 + delta if self.control_pressed: angle = (p2 - c).polar()[1] p2 = Polar(l2, angle) + c self.p2 = p2 if selection == 3 or self.shift_pressed: p3 = p3 + delta if self.control_pressed: angle = (p3 - c).polar()[1] p3 = Polar(l3, angle) + c self.p3 = p3 self.recompute()
def GetHandles(self): h1, h2, h3, theta, p1, p2 = self.h1, self.h2, self.h3, self.theta, \ self.p1, self.p2 xvect = (p2 - p1).normalized() angle = (p2 - p1).polar()[1] + theta * pi / 180 yvect = Polar(1, angle) handles = [] for p in (p1, p2): handles.append(MakeRoundHandle(self.trafo(p))) for p in (p1 + h1 * yvect, p2 + h2 * yvect, 0.5 * (p1 + p2) + h3 * yvect, p1 + h1 * yvect * 0.5, p2 + h2 * yvect * 0.5): handles.append(MakeControlHandle(self.trafo(p))) return handles
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 apply_constraint(self, p, state): if state & ConstraintMask: try: inverse = self.trafo.inverse() p2 = inverse(p) r, phi = p2.polar() pi12 = pi / 12 angle = pi12 * floor(phi / pi12 + 0.5) pi2 = 2 * pi d1 = fmod(abs(phi - angle), pi2) if self.selection == 1: selected_angle = self.end_angle else: selected_angle = self.start_angle d2 = fmod(abs(phi - selected_angle), pi2) if d2 < d1: phi = selected_angle else: phi = angle p = self.trafo(Polar(r, phi)) except SingularMatrix: pass return p
def recompute(self): p1 = Polar(self.l1, self.theta1 * pi / 180) p2 = Polar(self.l2, self.theta2 * pi / 180) new = Sketch.CreatePath() newpaths = [new] new.AppendLine(p1) new.AppendLine((0, 0)) new.AppendLine(p2) new.Transform(self.trafo) alpha = p1.polar()[1] beta = p2.polar()[1] if self.objects: self.objects[0].SetPaths(newpaths) self.objects[1].SetAngles(alpha, beta) trafo = self.trafo(Scale(self.r, self.r)) self.objects[1].set_transformation(trafo) else: lines = Sketch.PolyBezier(tuple(newpaths)) circle = Sketch.Ellipse(start_angle=alpha, end_angle=beta) circle.Transform(Scale(self.r, self.r)) self.set_objects([lines, circle])
def DragHandle(self, pa, pb, selection): try: inverse = self.trafo.inverse() except SingularMatrix: return pa, pb = map(inverse, (pa, pb)) delta = pb - pa h1, h2, h3, theta, p1, p2 = self.h1, self.h2, self.h3, self.theta, \ self.p1, self.p2 xvect = (p2 - p1).normalized() xnvect = Point(xvect.y, -xvect.x) angle = (p2 - p1).polar()[1] + theta * pi / 180 yvect = Polar(1, angle) sin_theta = xnvect * yvect if sin_theta == 0: h = delta * xvect else: h = (xnvect * delta) / sin_theta l, angle = (p1 - p2).polar() if selection == 1: p1 = p1 + delta if self.shift_pressed: s, angle = (p1 - p2).polar() p1 = (p1 - p2) * l / s + p2 if self.control_pressed: s, angle = (p1 - p2).polar() theta = int(theta * 180 / pi / 15 + 0.5) * pi / 180 * 15. p1 = p2 + s * Polar(1, angle) self.p1 = p1 elif selection == 2: l, angle = (p1 - p2).polar() p2 = p2 + delta if self.shift_pressed: s, angle = (p2 - p1).polar() p2 = (p2 - p1) * l / s + p1 if self.control_pressed: s, angle = (p2 - p1).polar() theta = int(theta * 180 / 15 / pi + 0.5) * pi / 180 * 15. p2 = p1 + s * Polar(1, theta) self.p2 = p2 elif selection == 3: self.h1 = h1 + h elif selection == 4: self.h2 = h2 + h elif selection == 5: # the middle line h3 = max(0, h3 + h) if h3 > h1: h3 = h1 if h3 > h2: h3 = h2 self.h3 = h3 elif selection == 6: g = p1 + h1 * yvect * 0.5 p = g + delta angle = (p - p1).polar()[1] - (p2 - p1).polar()[1] self.theta = 180 * angle / pi elif selection == 7: g = p2 + h2 * yvect * 0.5 p = g + delta angle = (p - p2).polar()[1] - (p2 - p1).polar()[1] self.theta = 180 * angle / pi self.recompute()
def DragHandle(self, pa, pb, selection): try: inverse = self.trafo.inverse() except SingularMatrix: return pa, pb = map(inverse, (pa, pb)) delta = pb-pa r1 = self.r1 r2 = self.r2 theta1 = self.theta1 % 360 theta2 = self.theta2 % 360 p1 = Polar(r1, theta1*pi/180) p2 = Polar(r2, theta1*pi/180) p3 = Polar(r1, theta2*pi/180) p4 = Polar(r2, theta2*pi/180) p5 = 0.5*(p1+p2) p6 = 0.5*(p3+p4) if theta2 < theta1: theta2 = theta2+360 if selection == 1: p1 = p1 + delta self.r1, angle = p1.polar() angle = angle*180/pi % 360 if angle < theta1-2 or angle > theta2+2: self.r1 = 0 if not self.shift_pressed: self.theta1 = angle elif selection == 2: p2 = p2 + delta self.r2, angle = p2.polar() if not self.shift_pressed: self.theta1 = angle*180/pi elif selection == 3: p3 = p3 + delta self.r1, angle = p3.polar() angle = angle*180/pi % 360 if angle < theta1-2 or angle > theta2+2: self.r1 = 0 if not self.shift_pressed: self.theta2 = angle elif selection == 4: p4 = p4 + delta self.r2, angle = p4.polar() if not self.shift_pressed: self.theta2 = angle*180/pi elif selection == 5: p5 = p5 + delta angle = p5.polar()[1] self.theta1 = angle*180/pi elif selection == 6: p6 = p6 + delta angle = p6.polar()[1] self.theta2 = angle*180/pi if self.control_pressed: if selection in (1,2,5): self.theta1 = int(0.5+self.theta1/5.)*5 elif selection in (3,4,6): self.theta2 = int(0.5+self.theta2/5.)*5 self.recompute()