Ejemplo n.º 1
0
def get_cap_segment(startpoint, endpoint, captype):
    """
    This returns a list of bezier segments that form the end cap of
    a line. Valid captypes are: CAP_BUTT, CAP_ROUND, CAP_SQUARE	
    """

    #  =====|
    if captype == sk2const.CAP_BUTT:
        return [startpoint, endpoint]

    # =====)
    elif captype == sk2const.CAP_ROUND:
        f = CIRCLE_CONSTANT
        d = mult_point(sub_points(endpoint, startpoint), 0.5)
        o = [d[1], -d[0]]

        new_p1 = add_points(startpoint, o)
        new_p2 = mult_point(o, f)

        return [
            startpoint,
            add_points(startpoint, new_p2),
            add_points(new_p1, mult_point(d, 1 - f)),
            add_points(new_p1, d),
            add_points(new_p1, mult_point(d, 1 + f)),
            add_points(endpoint, new_p2), endpoint
        ]

    # =====]
    elif captype == sk2const.CAP_SQUARE:
        d = mult_point(sub_points(endpoint, startpoint), 0.5)
        o = [d[1], -d[0]]

        new_p1 = add_points(startpoint, o)
        new_p2 = add_points(endpoint, o)

        return [
            startpoint, startpoint, new_p1, new_p1, new_p1, new_p2, new_p2,
            new_p2, endpoint, endpoint
        ]

    else:
        raise 'Unknown captype %d' % captype
Ejemplo n.º 2
0
def create_stroke_outline(path, radius, linejoin=sk2const.JOIN_MITER,
                          captype=sk2const.CAP_BUTT, miter_limit=MITER_LIMIT):
    """
    Outlines a single stroke. Returns two lists of lists of bezier
    segments for both sides of the stroke.
    """
    fw_segments = []
    bw_segments = []

    last_point = None

    segs = [path[0], ] + path[1]
    startpoint = [] + path[0]
    for i in range(len(segs)):
        segment = unpack_seg(segs[i], startpoint)
        startpoint = bezier_base_point(segs[i])
        if not segment[0]:
            if last_point:
                c1 = sub_points(segment[2], last_point)
                if not c1 == [0.0]:
                    t1 = mult_point(normalize(c1), radius)
                    fw_segments.append(
                        [add_points(last_point, [t1[1], -t1[0]]),
                         add_points(segment[2], [t1[1], -t1[0]])])
                    bw_segments.insert(0,
                                       [sub_points(segment[2], [t1[1], -t1[0]]),
                                        sub_points(last_point,
                                                   [t1[1], -t1[0]])])
            last_point = segment[2]

        else:
            segments = build_parallel([last_point, segment[1][0],
                                       segment[1][1], segment[2]], radius)
            fw_segments.append(segments)

            segments = build_parallel([segment[2], segment[1][1],
                                       segment[1][0], last_point], radius)
            bw_segments.insert(0, segments)
            last_point = segment[2]

    # Connect segments if necessary
    for item in [fw_segments, bw_segments]:
        join_segs(item, radius, linejoin, miter_limit,
                  path[2] == sk2const.CURVE_CLOSED)

    # Set caps for unclosed paths
    if not path[2] == sk2const.CURVE_CLOSED:
        fw_segments.insert(0, get_cap_segment(bw_segments[-1][-1],
                                              fw_segments[0][0], captype))
        bw_segments.insert(0, get_cap_segment(fw_segments[-1][-1],
                                              bw_segments[0][0], captype))

    return fw_segments, bw_segments
Ejemplo n.º 3
0
def get_cap_segment (startpoint, endpoint, captype):
	"""
	This returns a list of bezier segments that form the end cap of
	a line. Valid captypes are: CAP_BUTT, CAP_ROUND, CAP_SQUARE	
	"""

	#  =====|
	if captype == sk2_const.CAP_BUTT:
		return [startpoint, endpoint]

	#  =====)
	elif captype == sk2_const.CAP_ROUND:
		f = CIRCLE_CONSTANT
		d = mult_point(sub_points(endpoint, startpoint), 0.5)
		o = [d[1], -d[0]]

		new_p1 = add_points(startpoint, o)
		new_p2 = mult_point(o, f)

		return [startpoint,
				add_points(startpoint, new_p2),
				add_points(new_p1, mult_point(d, 1 - f)),
				add_points(new_p1, d),
				add_points(new_p1, mult_point(d, 1 + f)),
				add_points(endpoint, new_p2),
				endpoint]

	#  =====]
	elif captype == sk2_const.CAP_SQUARE:
		d = mult_point(sub_points(endpoint, startpoint), 0.5)
		o = [d[1], -d[0]]

		new_p1 = add_points(startpoint, o)
		new_p2 = add_points(endpoint, o)

		return [startpoint, startpoint, new_p1, new_p1, new_p1,
				new_p2, new_p2, new_p2, endpoint, endpoint]

	else:
		raise "Unknown captype %d" % captype
Ejemplo n.º 4
0
def build_parallel(p, radius, recursionlimit=6):
    """
	This builds a list of bezier segments that are "sufficiently"
	close to a given source segment. It recursively subdivides, if
	the check for parallelity fails.	
	"""
    # find tangent to calculate orthogonal neighbor of endpoint
    for i in p[1:]:
        c1 = sub_points(i, p[0])
        if c1: break

    if c1 == [0, 0]: return []

    t1 = mult_point(normalize(c1), radius)
    p0 = add_points(p[0], [t1[1], -t1[0]])
    c1 = sub_points(p[1], p[0])

    for i in [p[2], p[1], p[0]]:
        c2 = sub_points(p[3], i)
        if not c2 == [0, 0]: break

    t2 = mult_point(normalize(c2), radius)
    p3 = add_points(p[3], [t2[1], -t2[0]])
    c2 = sub_points(p[3], p[2])

    sd = subdivide_seg(p)
    center = sd[3]
    ccenter = mult_point(normalize(sub_points(sd[4], sd[3])), radius)

    new_center = add_points(center, [ccenter[1], -ccenter[0]])
    seg = [p0, add_points(p0, c1), sub_points(p3, c2), p3]
    now_center = subdivide_seg(seg)[3]

    offset = mult_point(sub_points(new_center, now_center), 8.0 / 3)

    det = c1[0] * c2[1] - c1[1] * c2[0]
    if det:
        ndet = det / length(c1) / length(c2)
    else:
        ndet = 0

    if math.fabs(ndet) >= 0.1:
        # "sufficiently" linear independent, cramers rule:
        oc1 = mult_point(c1, ((offset[0] * c2[1] - offset[1] * c2[0]) / det))
        oc2 = mult_point(c2, ((c1[0] * offset[1] - c1[1] * offset[0]) / det))
    else:
        # don't bother to try to correct the error, will figure out
        # soon if subdivision is necessary.
        oc1 = [0.0, 0.0]
        oc2 = [0.0, 0.0]

    new_p1 = add_points(add_points(p0, c1), oc1)
    new_p2 = add_points(sub_points(p3, c2), oc2)
    proposed_segment = [p0, new_p1, new_p2, p3]
    if check_parallel(p, proposed_segment, radius) or recursionlimit <= 0:
        return proposed_segment
    else:
        # "Not parallel enough" - subdivide.
        return (build_parallel(sd[:4], radius, recursionlimit - 1) +
                build_parallel(sd[3:], radius, recursionlimit - 1)[1:])
Ejemplo n.º 5
0
def build_parallel(p, radius, recursionlimit=6):
	"""
	This builds a list of bezier segments that are "sufficiently"
	close to a given source segment. It recursively subdivides, if
	the check for parallelity fails.	
	"""
	# find tangent to calculate orthogonal neighbor of endpoint
	for i in p[1:]:
		c1 = sub_points(i, p[0])
		if c1: break

	if c1 == [0, 0]: return []

	t1 = mult_point(normalize(c1), radius)
	p0 = add_points(p[0], [t1[1], -t1[0]])
	c1 = sub_points(p[1], p[0])

	for i in [p[2], p[1], p[0]]:
		c2 = sub_points(p[3], i)
		if not c2 == [0, 0]: break

	t2 = mult_point(normalize(c2), radius)
	p3 = add_points(p[3], [t2[1], -t2[0]])
	c2 = sub_points(p[3], p[2])

	sd = subdivide_seg(p)
	center = sd[3]
	ccenter = mult_point(normalize(sub_points(sd[4], sd[3])), radius)

	new_center = add_points(center, [ccenter[1], -ccenter[0]])
	seg = [p0, add_points(p0, c1), sub_points(p3, c2), p3]
	now_center = subdivide_seg(seg)[3]

	offset = mult_point(sub_points(new_center, now_center), 8.0 / 3)

	det = c1[0] * c2[1] - c1[1] * c2[0]
	if det:
		ndet = det / length(c1) / length(c2)
	else:
		ndet = 0

	if math.fabs(ndet) >= 0.1:
		# "sufficiently" linear independent, cramers rule:
		oc1 = mult_point(c1, ((offset[0] * c2[1] - offset[1] * c2[0]) / det))
		oc2 = mult_point(c2, ((c1[0] * offset[1] - c1[1] * offset[0]) / det))
	else:
		# don't bother to try to correct the error, will figure out
		# soon if subdivision is necessary.
		oc1 = [0.0, 0.0]
		oc2 = [0.0, 0.0]

	new_p1 = add_points(add_points(p0, c1), oc1)
	new_p2 = add_points(sub_points(p3, c2), oc2)
	proposed_segment = [p0, new_p1, new_p2, p3]
	if check_parallel (p, proposed_segment, radius) or recursionlimit <= 0:
		return proposed_segment
	else:
		# "Not parallel enough" - subdivide.
		return (build_parallel (sd[:4], radius, recursionlimit - 1) +
				  build_parallel (sd[3:], radius, recursionlimit - 1)[1:])
Ejemplo n.º 6
0
def create_stroke_outline (path, radius, linejoin=sk2_const.JOIN_MITER,
						captype=sk2_const.CAP_BUTT, miter_limit=MITER_LIMIT):
	"""
	Outlines a single stroke. Returns two lists of lists of bezier
	segments for both sides of the stroke.
	"""
	fw_segments = []
	bw_segments = []

	last_point = None

	segs = [path[0], ] + path[1]
	startpoint = [] + path[0]
	for i in range (len(segs)):
		segment = unpack_seg(segs[i], startpoint)
		startpoint = bezier_base_point(segs[i])
		if not segment[0]:
			if last_point:
				c1 = sub_points(segment[2], last_point)
				if not c1 == [0.0]:
					t1 = mult_point(normalize(c1), radius)
					fw_segments.append(
								[add_points(last_point, [t1[1], -t1[0]]),
								 add_points(segment[2], [t1[1], -t1[0]])])
					bw_segments.insert(0,
								[sub_points(segment[2], [t1[1], -t1[0]]),
								 sub_points(last_point, [t1[1], -t1[0]])])
			last_point = segment[2]

		else:
			segments = build_parallel([last_point, segment[1][0],
										segment[1][1], segment[2]], radius)
			fw_segments.append(segments)

			segments = build_parallel([segment[2], segment[1][1],
										segment[1][0], last_point], radius)
			bw_segments.insert(0, segments)
			last_point = segment[2]

	# Connect segments if necessary
	for item in [fw_segments, bw_segments]:
		join_segs(item, radius, linejoin, miter_limit,
				path[2] == sk2_const.CURVE_CLOSED)

	# Set caps for unclosed paths
	if not path[2] == sk2_const.CURVE_CLOSED:
		fw_segments.insert(0, get_cap_segment (bw_segments[-1][-1],
									fw_segments[0][0], captype))
		bw_segments.insert(0, get_cap_segment (fw_segments[-1][-1],
									bw_segments[0][0], captype))

	return fw_segments, bw_segments
Ejemplo n.º 7
0
def check_parallel(source, parallel, radius, tolerance=0.01):
	"""
	This function checks, if two bezier segments are "sufficiently"
	parallel. It checks, if the points for the parameters 0.25, 0.5
	and 0.75 of the tested segment are orthogonal to the resp.
	points of the source segment. 1% tolerance is default.
	
	It does not check the start and endpoints, since they are
	assumed to be correct by construction.
	"""
	for t0 in [0.25, 0.5, 0.75]:
		s = subdivide_seg(source, t0)
		t = subdivide_seg(parallel, t0)
		ccenter = mult_point(normalize(sub_points(s[4], s[3])), radius)
		orig = add_points(s[3], [ccenter[1], -ccenter[0]])
		if length(sub_points(orig, t[3])) >= tolerance * radius: return False
	return True
Ejemplo n.º 8
0
def check_parallel(source, parallel, radius, tolerance=0.01):
    """
	This function checks, if two bezier segments are "sufficiently"
	parallel. It checks, if the points for the parameters 0.25, 0.5
	and 0.75 of the tested segment are orthogonal to the resp.
	points of the source segment. 1% tolerance is default.
	
	It does not check the start and endpoints, since they are
	assumed to be correct by construction.
	"""
    for t0 in [0.25, 0.5, 0.75]:
        s = subdivide_seg(source, t0)
        t = subdivide_seg(parallel, t0)
        ccenter = mult_point(normalize(sub_points(s[4], s[3])), radius)
        orig = add_points(s[3], [ccenter[1], -ccenter[0]])
        if length(sub_points(orig, t[3])) >= tolerance * radius: return False
    return True
Ejemplo n.º 9
0
def split_bezier_curve(start_point, end_point, t=0.5):
    p0 = [] + start_point
    if len(start_point) > 2:
        p0 = [] + start_point[2]
    p1, p2, p3, flag = deepcopy(end_point)
    p0_1 = add_points(mult_point(p0, (1.0 - t)), mult_point(p1, t))
    p1_2 = add_points(mult_point(p1, (1.0 - t)), mult_point(p2, t))
    p2_3 = add_points(mult_point(p2, (1.0 - t)), mult_point(p3, t))
    p01_12 = add_points(mult_point(p0_1, (1.0 - t)), mult_point(p1_2, t))
    p12_23 = add_points(mult_point(p1_2, (1.0 - t)), mult_point(p2_3, t))
    p0112_1223 = add_points(mult_point(p01_12, (1.0 - t)),
                            mult_point(p12_23, t))
    new_point = [p0_1, p01_12, p0112_1223, flag]
    new_end_point = [p12_23, p2_3, p3, flag]
    return new_point, new_end_point
Ejemplo n.º 10
0
def subdivide_seg(seg, t=0.5):
    """
    This subdivides a bezier segment at the (optional) Parameter t
    """
    assert len(seg) == 4
    t2 = 1.0 - t
    p01 = add_points(mult_point(seg[0], t2), mult_point(seg[1], t))
    p12 = add_points(mult_point(seg[1], t2), mult_point(seg[2], t))
    p23 = add_points(mult_point(seg[2], t2), mult_point(seg[3], t))
    p012 = add_points(mult_point(p01, t2), mult_point(p12, t))
    p123 = add_points(mult_point(p12, t2), mult_point(p23, t))
    p0123 = add_points(mult_point(p012, t2), mult_point(p123, t))
    return seg[0], p01, p012, p0123, p123, p23, seg[3]
Ejemplo n.º 11
0
 def split_seg(self, base_point, point, t=0.5):
     if is_bezier(point):
         p0 = base_point
         p1, p2, p3 = point[:3]
         t2 = 1.0 - t
         r = add_points(mult_point(p1, t2), mult_point(p2, t))
         q1 = add_points(mult_point(p0, t2), mult_point(p1, t))
         q2 = add_points(mult_point(q1, t2), mult_point(r, t))
         q5 = add_points(mult_point(p2, t2), mult_point(p3, t))
         q4 = add_points(mult_point(r, t2), mult_point(q5, t))
         q3 = add_points(mult_point(q2, t2), mult_point(q4, t))
         return [[q1, q2, [] + q3, 0], q3, [q4, q5, [] + p3, 0]]
     else:
         new_point = add_points(mult_point(base_point, (1.0 - t)),
                                mult_point(point, t))
         return [new_point, [] + new_point, None]
Ejemplo n.º 12
0
def get_join_segment(startpoint,
                     endpoint,
                     radius,
                     jointype,
                     miter_limit=MITER_LIMIT):
    """
    This returns a list of bezier segments that joins two points
    with a given radius (fails if the radius is smaller than the
    distance between startpoint) and endpoint). 
    jointype is one of 	JOIN_MITER, JOIN_ROUND, JOIN_BEVEL	
    """

    if jointype == sk2const.JOIN_MITER:
        d = mult_point(sub_points(endpoint, startpoint), 0.5)

        if d == [0, 0]:
            return []

        o = normalize([d[1], -d[0]])

        if radius < length(d):
            return [startpoint, endpoint]

        h = math.sqrt(radius**2 - length(d)**2)

        h2 = length(d)**2 / h

        if h2 + h > miter_limit * radius:
            # Hit miter limit
            return [startpoint, endpoint]

        edge = add_points(add_points(startpoint, d), mult_point(o, h2))
        new_seg1 = line_to_curve(startpoint, edge)
        new_seg2 = line_to_curve(edge, endpoint)
        return [
            startpoint,
        ] + new_seg1 + new_seg2

    elif jointype == sk2const.JOIN_ROUND:
        f = CIRCLE_CONSTANT
        d = mult_point(sub_points(endpoint, startpoint), 0.5)

        if d == [0, 0]:
            return []

        o = mult_point(normalize([d[1], -d[0]]), radius)

        if radius < length(d):
            return [startpoint, endpoint]

        h = math.sqrt(radius**2 - length(d)**2) / radius

        center = sub_points(add_points(startpoint, d), mult_point(o, h))
        d = mult_point(normalize(d), radius)

        t0 = circleparam(h)
        quadseg = [
            sub_points(center, d),
            add_points(sub_points(center, d), mult_point(o, f)),
            add_points(sub_points(center, mult_point(d, f)), o),
            add_points(center, o)
        ]
        ret = [startpoint] + list(subdivide_seg(quadseg, t0)[4:])

        quadseg = [
            add_points(center, o),
            add_points(add_points(center, o), mult_point(d, f)),
            add_points(add_points(center, d), mult_point(o, f)),
            add_points(center, d)
        ]
        ret = ret + list(subdivide_seg(quadseg, 1 - t0)[1:3]) + [endpoint]

        return ret

    elif jointype == sk2const.JOIN_BEVEL:
        return [startpoint, endpoint]

    else:
        raise "Unknown join type %d" % jointype
Ejemplo n.º 13
0
def split_bezier_curve(start_point, end_point, t=0.5):
    p0 = start_point[2] if len(start_point) > 2 else start_point
    p1, p2, p3 = end_point[:3]
    flag = end_point[3] if len(end_point) == 4 else sk2const.NODE_CUSP
    p0_1 = add_points(mult_point(p0, (1.0 - t)), mult_point(p1, t))
    p1_2 = add_points(mult_point(p1, (1.0 - t)), mult_point(p2, t))
    p2_3 = add_points(mult_point(p2, (1.0 - t)), mult_point(p3, t))
    p01_12 = add_points(mult_point(p0_1, (1.0 - t)), mult_point(p1_2, t))
    p12_23 = add_points(mult_point(p1_2, (1.0 - t)), mult_point(p2_3, t))
    p0112_1223 = add_points(mult_point(p01_12, (1.0 - t)),
                            mult_point(p12_23, t))
    new_point = [p0_1, p01_12, p0112_1223, flag]
    new_end_point = [p12_23, p2_3, p3, flag]
    return new_point, new_end_point
Ejemplo n.º 14
0
	def split_seg(self, base_point, point, t=0.5):
		if is_bezier(point):
			p0 = base_point
			p1, p2, p3 = point[:3]
			t2 = 1.0 - t
			r = add_points(mult_point(p1, t2) , mult_point(p2, t))
			q1 = add_points(mult_point(p0, t2), mult_point(p1, t))
			q2 = add_points(mult_point(q1, t2), mult_point(r, t))
			q5 = add_points(mult_point(p2, t2), mult_point(p3, t))
			q4 = add_points(mult_point(r, t2), mult_point(q5, t))
			q3 = add_points(mult_point(q2, t2), mult_point(q4, t))
			return [[q1, q2, [] + q3, 0], q3, [q4, q5, [] + p3, 0]]
		else:
			new_point = add_points(mult_point(base_point, (1.0 - t)),
						mult_point(point, t))
			return [new_point, [] + new_point, None]
Ejemplo n.º 15
0
def subdivide_seg(seg, t=0.5):
	"""
	This subdivides a bezier segment at the (optional) Parameter t
	"""
	assert len(seg) == 4
	t2 = 1.0 - t
	p01 = add_points(mult_point(seg[0], t2) , mult_point(seg[1], t))
	p12 = add_points(mult_point(seg[1], t2), mult_point(seg[2], t))
	p23 = add_points(mult_point(seg[2], t2), mult_point(seg[3], t))
	p012 = add_points(mult_point(p01, t2), mult_point(p12, t))
	p123 = add_points(mult_point(p12, t2), mult_point(p23, t))
	p0123 = add_points(mult_point(p012, t2), mult_point(p123, t))
	return (seg[0], p01, p012, p0123, p123, p23, seg[3])
Ejemplo n.º 16
0
def split_bezier_curve(start_point, end_point, t=0.5):
	p0 = [] + start_point
	if len(start_point) > 2: p0 = [] + start_point[2]
	p1, p2, p3, flag = deepcopy(end_point)
	p0_1 = add_points(mult_point(p0, (1.0 - t)), mult_point(p1, t))
	p1_2 = add_points(mult_point(p1, (1.0 - t)), mult_point(p2, t))
	p2_3 = add_points(mult_point(p2, (1.0 - t)), mult_point(p3, t))
	p01_12 = add_points(mult_point(p0_1, (1.0 - t)), mult_point(p1_2, t))
	p12_23 = add_points(mult_point(p1_2, (1.0 - t)), mult_point(p2_3, t))
	p0112_1223 = add_points(mult_point(p01_12, (1.0 - t)), mult_point(p12_23, t))
	new_point = [p0_1, p01_12, p0112_1223, flag]
	new_end_point = [p12_23, p2_3, p3, flag]
	return new_point, new_end_point
Ejemplo n.º 17
0
def get_join_segment(startpoint, endpoint, radius, jointype,
					miter_limit=MITER_LIMIT):
	"""
	This returns a list of bezier segments that joins two points
	with a given radius (fails if the radius is smaller than the
	distance between startpoint) and endpoint). 
	jointype is one of 	JOIN_MITER, JOIN_ROUND, JOIN_BEVEL	
	"""

	if jointype == sk2_const.JOIN_MITER:
		d = mult_point(sub_points(endpoint, startpoint), 0.5)

		if d == [0, 0]: return []

		o = normalize([d[1], -d[0]])
		h = math.sqrt(radius ** 2 - length(d) ** 2)

		h2 = length(d) ** 2 / h

		if h2 + h > miter_limit * radius:
			# Hit miter limit
			return [startpoint, endpoint]

		edge = add_points(add_points(startpoint, d), mult_point(o, h2))
		new_seg1 = line_to_curve(startpoint, edge)
		new_seg2 = line_to_curve(edge, endpoint)
		return [startpoint, ] + new_seg1 + new_seg2

	elif jointype == sk2_const.JOIN_ROUND:
		f = CIRCLE_CONSTANT
		d = mult_point(sub_points(endpoint, startpoint), 0.5)

		if d == [0, 0]: return []

		o = mult_point(normalize([d[1], -d[0]]), radius)

		h = math.sqrt(radius ** 2 - length(d) ** 2) / radius

		center = sub_points(add_points(startpoint, d), mult_point(o, h))
		d = mult_point(normalize(d), radius)

		t0 = circleparam(h)
		quadseg = [sub_points(center, d),
					add_points(sub_points(center, d), mult_point(o, f)),
					add_points(sub_points(center, mult_point(d, f)), o),
					add_points(center, o)]
		ret = [startpoint] + list (subdivide_seg(quadseg, t0)[4:])

		quadseg = [add_points(center, o),
					add_points(add_points(center, o), mult_point(d, f)),
					add_points(add_points(center, d), mult_point(o, f)),
					add_points(center, d)]
		ret = ret + list (subdivide_seg(quadseg, 1 - t0)[1:3]) + [endpoint]

		return ret

	elif jointype == sk2_const.JOIN_BEVEL:
		return [startpoint, endpoint]

	else:
		raise "Unknown join type %d" % jointype