def split_line_by_pattern(points, pattern): dg = DashGenerator(pattern) lines = [] is_whitespace = False current_line = [] for p in xrange(1, len(points)): start = vec2(points[p-1]) end = vec2(points[p]) normal = (end - start).normalized() amount_to_move = (end - start).length() current = start while amount_to_move > 0: l, should_flip = dg.next(amount_to_move) a = current b = current + normal * l current = b if not is_whitespace: if len(current_line): current_line.append(b) else: current_line.append(a) current_line.append(b) if should_flip: if not is_whitespace: lines.append(current_line) current_line = [] is_whitespace = not is_whitespace amount_to_move -= l if len(current_line): lines.append(current_line) return lines
def calculate_tangents(self): v = (self.end - self.start).normalized() angle = math.atan2(v.y, v.x) half_width = self.w * 0.5 self.up_normal = vec2(math.cos(angle - radian(90)) * half_width, math.sin(angle - radian(90)) * half_width) self.dn_normal = vec2(math.cos(angle + radian(90)) * half_width, math.sin(angle + radian(90)) * half_width)
def _render_stroke(self): stroke = self.style.stroke stroke_width = self.style.stroke_width is_miter = self.style.stroke_linejoin == 'miter' miter_limit = self.style.stroke_miterlimit if is_miter else 0 for loop in self.outlines: self.svg.n_lines += len(loop) - 1 loop_plus = [] for i in xrange(len(loop) - 1): loop_plus += [loop[i], loop[i+1]] if isinstance(stroke, str): g = self.svg._gradients[stroke] strokes = [g.sample(x, self) for x in loop_plus] else: strokes = [stroke for x in loop_plus] if len(loop_plus) == 0: continue if len(self.style.stroke_dasharray): ls = lines.split_line_by_pattern(loop_plus, self.style.stroke_dasharray) if ls[0][0] == ls[-1][-1]: #if the last line end point equals the first line start point, #this is a "closed" line, so combine the first and the last line combined_line = ls[-1] + ls[0] ls[0] = combined_line del ls[-1] for l in ls: lines.draw_polyline( l, stroke_width, color=strokes[0], line_cap=self.style.stroke_linecap, join_type=self.style.stroke_linejoin, miter_limit=miter_limit) if self.marker_start: end_point = vec2(loop_plus[0]) almost_end_point = vec2(loop_plus[1]) marker = self.svg.defs[self.marker_start] self._render_marker(end_point, almost_end_point, marker, True) if self.marker_end: end_point = vec2(loop_plus[-1]) almost_end_point = vec2(loop_plus[-2]) marker = self.svg.defs[self.marker_end] self._render_marker(end_point, almost_end_point, marker) else: lines.draw_polyline( loop_plus, stroke_width, color=strokes[0], line_cap=self.style.stroke_linecap, join_type=self.style.stroke_linejoin, miter_limit=miter_limit) if self.marker_start: end_point = vec2(loop_plus[0]) almost_end_point = vec2(loop_plus[1]) marker = self.svg.defs[self.marker_start] self._render_marker(end_point, almost_end_point, marker, True) if self.marker_end: end_point = vec2(loop_plus[-1]) almost_end_point = vec2(loop_plus[-2]) marker = self.svg.defs[self.marker_end] self._render_marker(end_point, almost_end_point, marker)
def _process_joint(ln, pln, miter_limit, rounded=False): up_intersection, ln.upper_join = ln_intersection(pln.upper_edge, ln.upper_edge) lo_intersection, ln.lower_join = ln_intersection(pln.lower_edge, ln.lower_edge) if up_intersection and lo_intersection: pln.upper_v.append(pln.upper_join) pln.lower_v.append(pln.lower_join) return if ln.upper_join == None: ln.upper_join = ln.upper_edge.start if ln.lower_join == None: ln.lower_join = ln.lower_edge.start ml1 = line_length(ln.lower_edge.start, ln.upper_join) if rounded: pass if rounded and not up_intersection: ln.upper_join = ln.upper_edge.start pln.upper_v.append(pln.upper_join) pln.upper_v.append(pln.upper_edge.end) #arc to next lines upper-join base = pln.end start = pln.upper_edge.end target = ln.upper_join dist = (start-base).length() av = (start - base).normalized() bv = (target - base).normalized() start_angle = av.angle() target_angle = bv.angle() if start_angle > target_angle: start_angle -= 2.0 * math.pi theta = start_angle pln.lower_v.append(pln.lower_join) while theta < target_angle: v = base + (vec2(math.cos(theta), math.sin(theta)) * dist) pln.upper_v.append(v) pln.lower_v.append(ln.lower_join) theta += 0.2 pln.upper_v.append(ln.upper_join) return elif ml1 > miter_limit and not up_intersection: #bevel ln.upper_join = ln.upper_edge.start pln.upper_v.append(pln.upper_join) pln.upper_v.append(pln.upper_edge.end) pln.upper_v.append(ln.upper_join) elif not rounded: pln.upper_v.append(pln.upper_join) pln.upper_v.append(ln.upper_join) ml2 = line_length(ln.upper_edge.start, ln.lower_join) if rounded and not lo_intersection: ln.lower_join = ln.lower_edge.start pln.upper_v.append(pln.upper_join) pln.lower_v.append(pln.lower_join) #arc to next lines upper-join base = pln.end start = pln.lower_edge.end target = ln.lower_join dist = (start-base).length() av = (start - base).normalized() bv = (target - base).normalized() start_angle = av.angle() target_angle = bv.angle() if start_angle < target_angle: start_angle += 2.0 * math.pi theta = start_angle pln.upper_v.append(ln.upper_join) while theta > target_angle: v = base + (vec2(math.cos(theta), math.sin(theta)) * dist) pln.lower_v.append(v) pln.upper_v.append(ln.upper_join) theta -= 0.2 pln.lower_v.append(ln.lower_join) elif ml2 > miter_limit and not lo_intersection: #bevel ln.lower_join = ln.lower_edge.start pln.lower_v.append(pln.lower_join) pln.lower_v.append(pln.lower_edge.end) pln.lower_v.append(ln.lower_join) else: pln.lower_v.append(pln.lower_join) pln.lower_v.append(ln.lower_join)
def calc_polyline(points, w, line_cap='butt', join_type='miter', miter_limit=4, closed=False): miter_length = w * miter_limit points = [vec2(p) for p in points] if closed and points[0] != points[-1]: points.append(vec2(points[0])) lines = [] for i in range(len(points) - 1): lines.append( LineSegment(points[i], points[i+1], w)) lines[0].upper_join = lines[0].upper_edge.start lines[0].lower_join = lines[0].lower_edge.start if line_cap == 'square' and not closed: ext = lines[0].direction * w * -0.5 lines[0].upper_join = lines[0].upper_join + ext lines[0].lower_join = lines[0].lower_join + ext for i in range(1, len(lines)): ln, pln = lines[i], lines[i-1] _process_joint(ln, pln, miter_length, join_type=='round') ll = lines[-1] lf = lines[0] if closed: b_up_int, upper_join = ln_intersection(ll.upper_edge, lf.upper_edge) b_lo_int, lower_join = ln_intersection(ll.lower_edge, lf.lower_edge) if upper_join == None: upper_join = ll.upper_edge.end if lower_join == None: lower_join = ll.lower_edge.end if line_length(ll.lower_edge.end, upper_join) > miter_length and b_up_int: #bevel ll.upper_v.append(ll.upper_join) ll.upper_v.append(ll.upper_edge.end) ll.upper_v.append(lf.upper_edge.start) else: lf.upper_v[0] = upper_join ll.upper_v.append(ll.upper_join) ll.upper_v.append(upper_join) if line_length(ll.upper_edge.end, lower_join) > miter_length and b_lo_int: #bevel ll.lower_v.append(ll.lower_join) ll.lower_v.append(ll.lower_edge.end) ll.lower_v.append(lf.lower_edge.start) else: lf.lower_v[0] = lower_join ll.lower_v.append(ll.lower_join) ll.lower_v.append(lower_join) else: if line_cap == 'butt' or line_cap == 'round': ll.upper_v.append(ll.upper_join) ll.upper_v.append(ll.upper_edge.end) ll.lower_v.append(ll.lower_join) ll.lower_v.append(ll.lower_edge.end) elif line_cap == 'square': ext = ll.direction * w*0.5 ll.upper_v.append(ll.upper_join) ll.upper_v.append(ll.upper_edge.end + ext) ll.lower_v.append(ll.lower_join) ll.lower_v.append(ll.lower_edge.end + ext) return lines