Example #1
0
/* one new saddle face with 4 edges */
void newsad (struct surface *this_srf, struct arc *arc1, struct arc *arc2, struct circle *circle1, struct circle *circle2, struct torus *torus_ptr, double wrap_angle)
{
	struct vertex *vertex1, *vertex2, *vertex3, *vertex4;
	struct arc *arc3, *arc4;
	struct face *saddle_fac;
	struct sphere *atom1, *atom2;

	atom1 = circle1 -> atm;
	atom2 = circle2 -> atm;

	/* gather vertices */
	vertex1 = arc1 -> vtx[0];
	vertex2 = arc1 -> vtx[1];
	vertex3 = arc2 -> vtx[0];
	vertex4 = arc2 -> vtx[1];

	/* set up arcs */
	arc3 = new_arc (circle2, vertex2, vertex3, CONVEX, 0, (double) 0.0, 0L, 0L, 0L);
	if (error()) return;
	this_srf -> n_arc++;
	arc4 = new_arc (circle1, vertex4, vertex1, CONVEX, 0, (double) 0.0, 0L, 0L, 0L);
	if (error()) return;
	this_srf -> n_arc++;
	arc3 -> phi = wrap_angle;
	arc4 -> phi = wrap_angle;

	/* add convex arcs to atom list */
	arc3 -> next = atom2 -> first_arc;
	atom2 -> first_arc = arc3;
	arc4 -> next = atom1 -> first_arc;
	atom1 -> first_arc = arc4;

	/* set up saddle face */
	saddle_fac = new_face (NULL, SADDLE); if (error()) return;
	saddle_fac -> ptr.tor = torus_ptr;
	link_face (this_srf, saddle_fac); if (error()) return;
	add2face (saddle_fac, arc1, arc3, arc2, arc4); if (error()) return;
}
Example #2
0
/* free (no collision) torus saddle surface */
void free_saddle (struct surface *this_srf, struct torus *torus_ptr)
{
	struct face *saddle_fac;
	struct circle *circle1, *circle2;
	struct arc *arc1, *arc2;
	struct sphere *atom1, *atom2;

	atom1 = (torus_ptr -> atm[0]);
	atom2 = (torus_ptr -> atm[1]);

	/* set up circles */

	circle1 = new_contact_circle (this_srf, torus_ptr, 0); if (error()) return;
	circle2 = new_contact_circle (this_srf, torus_ptr, 1); if (error()) return;

	/* set up arcs */
	arc1 = new_arc (circle2, NULL, NULL, CONVEX, 0, (double) 0.0, 0L, 0L, 0L);
	if (error()) return;
	this_srf -> n_arc++;
	arc2 = new_arc (circle1, NULL, NULL, CONVEX, 0, (double) 0.0, 0L, 0L, 0L);
	if (error()) return;
	this_srf -> n_arc++;
	arc1 -> phi = 2 * PI;
	arc2 -> phi = 2 * PI;

	/* add convex arcs to atom list */
	arc1 -> next = atom2 -> first_arc;
	atom2 -> first_arc = arc1;
	arc2 -> next = atom1 -> first_arc;
	atom1 -> first_arc = arc2;

	/* allocate saddle face */
	saddle_fac = new_face (NULL, SADDLE); if (error()) return;
	saddle_fac -> ptr.tor = torus_ptr;
	link_face (this_srf, saddle_fac); if (error()) return;
	add2face (saddle_fac, arc1, arc2, NULL, NULL); if (error()) return;
}
Example #3
0
/* 
 *  Read symbol specified by name.
 *  Name: group/name | group/name@mapset 
 *        (later add syntax to prefer symbol from GISBASE)
 *  S_read() searches first in mapsets (standard GRASS search) and
 *   then in GISBASE/etc/symbol/ 
 */
SYMBOL *S_read(const char *sname)
{
    int i, j, k, l;
    FILE *fp;
    char group[500], name[500], buf[2001];
    const char *ms;
    char *c;
    double x, y, x2, y2, rad, ang1, ang2;
    int r, g, b;
    double fr, fg, fb;
    int ret;
    char clock;
    SYMBOL *symb;
    int current;		/* current part_type */
    SYMBPART *part;		/* current part */
    SYMBCHAIN *chain;		/* current chain */
    SYMBEL *elem;		/* current element */

    G_debug(3, "S_read(): sname = %s", sname);

    /* Find file */
    /* Get group and name */
    strcpy(group, sname);
    c = strchr(group, '/');
    if (c == NULL) {
	G_warning(_("Incorrect symbol name: '%s' (should be: group/name or group/name@mapset)"),
		  sname);
	return NULL;
    }
    c[0] = '\0';

    c++;
    strcpy(name, c);

    G_debug(3, "  group: '%s' name: '%s'", group, name);

    /* Search in mapsets */
    sprintf(buf, "symbol/%s", group);
    ms = G_find_file(buf, name, NULL);

    if (ms != NULL) {		/* Found in mapsets */
	fp = G_fopen_old(buf, name, ms);
    }
    else {			/* Search in GISBASE */
	sprintf(buf, "%s/etc/symbol/%s", G_gisbase(), sname);
	fp = fopen(buf, "r");
    }

    if (fp == NULL) {
	G_warning(_("Cannot find/open symbol: '%s'"), sname);
	return NULL;
    }

    /* create new symbol */
    symb = new_symbol();

    current = OBJ_NONE;		/* no part */

    /* read file */
    while (G_getl2(buf, 2000, fp) != 0) {
	G_chop(buf);
	G_debug(3, "  BUF: [%s]", buf);

	/* skip empty and comment lines */
	if ((buf[0] == '#') || (buf[0] == '\0'))
	    continue;

	get_key_data(buf);

	if (strcmp(key, "VERSION") == 0) {
	    if (strcmp(data, "1.0") != 0) {
		sprintf(buf, "Wrong symbol version: '%s'", data);
		return (err(fp, symb, buf));
	    }
	}
	else if (strcmp(key, "BOX") == 0) {
	    if (sscanf(data, "%lf %lf %lf %lf", &x, &y, &x2, &y2) != 4) {
		sprintf(buf, "Incorrect box definition: '%s'", data);
		return (err(fp, symb, buf));
	    }
	    symb->xscale = 1 / (x2 - x);
	    symb->yscale = 1 / (y2 - y);
	    if (x2 - x > y2 - y) {
		symb->scale = symb->xscale;
	    }
	    else {
		symb->scale = symb->yscale;
	    }
	}
	else if (strcmp(key, "STRING") == 0) {
	    G_debug(4, "  STRING >");
	    current = OBJ_STRING;
	    part = new_part(S_STRING);
	    add_part(symb, part);

	    chain = new_chain();
	    add_chain(part, chain);
	}
	else if (strcmp(key, "POLYGON") == 0) {
	    G_debug(4, "  POLYGON >");
	    current = OBJ_POLYGON;
	    part = new_part(S_POLYGON);
	    add_part(symb, part);

	}
	else if (strcmp(key, "RING") == 0) {
	    G_debug(4, "  RING >");
	    current = OBJ_RING;
	    chain = new_chain();
	    add_chain(part, chain);

	}
	else if (strcmp(key, "LINE") == 0) {
	    G_debug(4, "    LINE >");
	    elem = new_line();
	    add_element(chain, elem);
	    read_coor(fp, elem);

	}
	else if (strcmp(key, "ARC") == 0) {
	    G_debug(4, "    ARC");
	    ret =
		sscanf(data, "%lf %lf %lf %lf %lf %c", &x, &y, &rad, &ang1,
		       &ang2, &clock);
	    if (ret < 5) {
		sprintf(buf, "Incorrect arc definition: '%s'", buf);
		return (err(fp, symb, buf));
	    }
	    if (ret == 6 && (clock == 'c' || clock == 'C'))
		i = 1;
	    else
		i = 0;
	    elem = new_arc(x, y, rad, ang1, ang2, i);
	    add_element(chain, elem);

	}
	else if (strcmp(key, "END") == 0) {
	    switch (current) {
	    case OBJ_STRING:
		G_debug(4, "  STRING END");
		current = OBJ_NONE;
		break;
	    case OBJ_POLYGON:
		G_debug(4, "  POLYGON END");
		current = OBJ_NONE;
		break;
	    case OBJ_RING:
		G_debug(4, "  RING END");
		current = OBJ_POLYGON;
		break;
	    }
	}
	else if (strcmp(key, "COLOR") == 0) {
	    if (G_strcasecmp(data, "NONE") == 0) {
		part->color.color = S_COL_NONE;
	    }
	    else if (sscanf(data, "%d %d %d", &r, &g, &b) == 3) {
		if (r < 0 || r > 255 || g < 0 || g > 255 || b < 0 || b > 255)
		    G_warning(_("Incorrect symbol color: '%s', using default."),
			      buf);
		else {
		    fr = r / 255.0;
		    fg = g / 255.0;
		    fb = b / 255.0;
		    part->color.color = S_COL_DEFINED;
		    part->color.r = r;
		    part->color.g = g;
		    part->color.b = b;
		    part->color.fr = fr;
		    part->color.fg = fg;
		    part->color.fb = fb;
		    G_debug(4, "  color [%d %d %d] = [%.3f %.3f %.3f]", r, g,
			    b, fr, fg, fb);
		}
	    }
	    else {
		G_warning(_("Incorrect symbol color: '%s', using default."),
			  buf);
	    }
	}
	else if (strcmp(key, "FCOLOR") == 0) {
	    if (G_strcasecmp(data, "NONE") == 0) {
		part->fcolor.color = S_COL_NONE;
	    }
	    else if (sscanf(data, "%d %d %d", &r, &g, &b) == 3) {
		if (r < 0 || r > 255 || g < 0 || g > 255 || b < 0 || b > 255)
		    G_warning(_("Incorrect symbol color: '%s', using default."),
			      buf);
		else {
		    fr = r / 255.0;
		    fg = g / 255.0;
		    fb = b / 255.0;
		    part->fcolor.color = S_COL_DEFINED;
		    part->fcolor.r = r;
		    part->fcolor.g = g;
		    part->fcolor.b = b;
		    part->fcolor.fr = fr;
		    part->fcolor.fg = fg;
		    part->fcolor.fb = fb;
		    G_debug(4, "  color [%d %d %d] = [%.3f %.3f %.3f]", r, g,
			    b, fr, fg, fb);
		}
	    }
	    else {
		G_warning(_("Incorrect symbol color: '%s', using default."),
			  buf);
	    }
	}
	else {
	    sprintf(buf, "Unknown keyword in symbol: '%s'", buf);
	    return (err(fp, symb, buf));
	    break;
	}
    }

    /* Debug output */

    G_debug(3, "Number of parts: %d", symb->count);
    for (i = 0; i < symb->count; i++) {
	part = symb->part[i];
	G_debug(4, "  Part %d: type: %d number of chains: %d", i, part->type,
		part->count);
	G_debug(4, "           color: %d: fcolor: %d", part->color.color,
		part->fcolor.color);
	for (j = 0; j < part->count; j++) {
	    chain = part->chain[j];
	    G_debug(4, "    Chain %d: number of elements: %d", j,
		    chain->count);
	    for (k = 0; k < chain->count; k++) {
		elem = chain->elem[k];
		G_debug(4, "      Element %d: type: %d", k, elem->type);
		if (elem->type == S_LINE) {
		    G_debug(4, "        Number of points %d",
			    elem->coor.line.count);
		    for (l = 0; l < elem->coor.line.count; l++) {
			G_debug(4, "        x, y: %f %f",
				elem->coor.line.x[l], elem->coor.line.y[l]);
		    }
		}
		else {
		    G_debug(4, "        arc r = %f", elem->coor.arc.r);
		}
	    }
	}
    }

    fclose(fp);

    return symb;
}
Example #4
0
/* new concave face */
void new_concave_face (struct surface *this_srf, struct probe *prb)
{
    int k;
    double probe_center[3];
	double vertex1_coor[3], vertex2_coor[3], vertex3_coor[3], vertex4_coor[3];
	double circle1_axis[3], circle2_axis[3], circle3_axis[3], circle4_axis[3];
	struct circle *circle1, *circle2, *circle3, *circle4;
	struct vertex *vertex1, *vertex2, *vertex3, *vertex4;
	struct arc *arc1, *arc2, *arc3, *arc4;
    struct sphere *atom1, *atom2, *atom3, *atom4;
	struct face *concave_fac;
    struct pair *torus1, *torus2, *torus3, *torus4;
	double dot1, dot2, dot3, dot4;
	char message[MAX_STRING];
    struct cept *ex;

	if (prb -> natom > 3) {
		sprintf (message, "%8ld probe square concave face", prb -> number);
		informd (message);
	}
	else {
		sprintf (message, "%8ld probe triangular concave face", prb -> number);
		informd2 (message);
	}
    atom1 = prb -> atm[0];
    atom2 = prb -> atm[1];
    atom3 = prb -> atm[2];
    atom4 = prb -> atm[3];
    torus1 = prb -> pairs[0];
    torus2 = prb -> pairs[1];
    torus3 = prb -> pairs[2];
    torus4 = prb -> pairs[3];
    for (k = 0; k < 3; k++)
		probe_center[k] = prb -> center[k];

	/* compute vertex coordinates */
	for (k = 0; k < 3; k++) {
		vertex1_coor[k] = (atom1 -> radius * probe_center[k] +
			this_srf -> probe_radius * atom1 -> center[k]) /
			(atom1 -> radius + this_srf -> probe_radius);
		vertex2_coor[k] = (atom2 -> radius * probe_center[k] +
			this_srf -> probe_radius * atom2 -> center[k]) /
			(atom2 -> radius + this_srf -> probe_radius);
		vertex3_coor[k] = (atom3 -> radius * probe_center[k] +
			this_srf -> probe_radius * atom3 -> center[k]) /
				(atom3 -> radius + this_srf -> probe_radius);
		if (atom4 != NULL) {
			vertex4_coor[k] = (atom4 -> radius * probe_center[k] +
				this_srf -> probe_radius * atom4 -> center[k]) /
					(atom4 -> radius + this_srf -> probe_radius);
		}
	}

	/* set up vertices */
	vertex1 = new_vertex (vertex1_coor, (struct sphere *) atom1, prb, NULL, NULL);
	if (vertex1 == NULL) return;
	link_vertex (this_srf, vertex1);
	vertex2 = new_vertex (vertex2_coor, (struct sphere *) atom2, prb, NULL, NULL);
	if (vertex2 == NULL) return;
	link_vertex (this_srf, vertex2);
	vertex3 = new_vertex (vertex3_coor, (struct sphere *) atom3, prb, NULL, NULL);
	if (vertex3 == NULL) return;
	link_vertex (this_srf, vertex3);
	if (atom4 != NULL) {
		vertex4 = new_vertex (vertex4_coor, (struct sphere *) atom4, prb, NULL, NULL);
		if (vertex4 == NULL) return;
		link_vertex (this_srf, vertex4);
	}
	else vertex4 = NULL;

	/* calculate axes and set up circles */
	setup_axis (probe_center, prb -> atm[0] -> center,
		prb -> atm[1] -> center, circle1_axis);
	setup_axis (probe_center, prb -> atm[1] -> center,
		prb -> atm[2] -> center, circle2_axis);
	if (atom4 == NULL) {
		setup_axis (probe_center, prb -> atm[2] -> center,
			prb -> atm[0] -> center, circle3_axis);
	}
	else if (atom4 != NULL) {
		setup_axis (probe_center, prb -> atm[2] -> center,
			prb -> atm[3] -> center, circle3_axis);
		setup_axis (probe_center, prb -> atm[3] -> center,
			prb -> atm[0] -> center, circle4_axis);
	}

	circle1 = new_circle (probe_center, this_srf -> probe_radius, circle1_axis);
	if (circle1 == NULL) return;
	link_circle (this_srf, circle1);
	circle1 -> theta = 0.0;
	circle1 -> subtype = GREAT_SUBTYPE;
	circle2 = new_circle (probe_center, this_srf -> probe_radius, circle2_axis);
	if (circle2 == NULL) return;
	link_circle (this_srf, circle2);
	circle2 -> theta = 0.0;
	circle2 -> subtype = GREAT_SUBTYPE;
	circle3 = new_circle (probe_center, this_srf -> probe_radius, circle3_axis);
	if (circle3 == NULL) return;
	link_circle (this_srf, circle3);
	circle3 -> theta = 0.0;
	circle3 -> subtype = GREAT_SUBTYPE;
	if (atom4 != NULL) {
		circle4 = new_circle (probe_center, this_srf -> probe_radius, circle4_axis);
		if (circle4 == NULL) return;
		link_circle (this_srf, circle4);
		circle4 -> theta = 0.0;
		circle4 -> subtype = GREAT_SUBTYPE;
	}
	else circle4 = NULL;

	/* set up arcs */
	arc1 = new_arc (circle1, vertex1, vertex2, CONCAVE, 0, (double) 0.0, 0L, 0L, 0L);
	if (arc1 == NULL) return;
	this_srf -> n_arc++;
	arc2 = new_arc (circle2, vertex2, vertex3, CONCAVE, 0, (double) 0.0, 0L, 0L, 0L);
	if (arc2 == NULL) return;
	this_srf -> n_arc++;
	if (circle4 == NULL) {
		arc3 = new_arc (circle3, vertex3, vertex1, CONCAVE, 0, (double) 0.0, 0L, 0L, 0L);
		if (arc3 == NULL) return;
		this_srf -> n_arc++;
		arc4 = NULL;
	}
	else if (circle4 != NULL) {
		arc3 = new_arc (circle3, vertex3, vertex4, CONCAVE, 0, (double) 0.0, 0L, 0L, 0L);
		if (arc3 == NULL) return;
		this_srf -> n_arc++;
		arc4 = new_arc (circle4, vertex4, vertex1, CONCAVE, 0, (double) 0.0, 0L, 0L, 0L);
		if (arc4 == NULL) return;
		this_srf -> n_arc++;
	}

	/* add arcs to tori */
	arc1 -> next = torus1 -> first_arc;
	torus1 -> first_arc = arc1;
	sprintf (message, "%8ld %8ld torus: add arc", 
		torus1 -> sph[0] -> number, torus1 -> sph[1] -> number);
	informd2(message);
	arc2 -> next = torus2 -> first_arc;
	torus2 -> first_arc = arc2;
	sprintf (message, "%8ld %8ld torus: add arc", 
		torus2 -> sph[0] -> number, torus2 -> sph[1] -> number);
	informd2(message);
	arc3 -> next = torus3 -> first_arc;
	torus3 -> first_arc = arc3;
	sprintf (message, "%8ld %8ld torus: add arc", 
		torus3 -> sph[0] -> number, torus3 -> sph[1] -> number);
	informd2(message);
	if (torus4 != NULL) {
		arc4 -> next = torus4 -> first_arc;
		torus4 -> first_arc = arc4;
		sprintf (message, "%8ld %8ld torus: add arc", 
			torus4 -> sph[0] -> number, torus4 -> sph[1] -> number);
		informd(message);
	}

	/* new concave face */
	concave_fac = new_face (NULL, CONCAVE);
	if (concave_fac == NULL) return;
	concave_fac -> ptr.prb = prb;
	concave_fac -> chi = 1;
	link_face (this_srf, concave_fac);
	add2face (concave_fac, arc1, arc2, arc3, arc4);

	/* back pointer for cusp corrections */
	prb -> fac = concave_fac;
	arc1 -> fac = concave_fac;
	arc2 -> fac = concave_fac;
	arc3 -> fac = concave_fac;
	if (arc4 != NULL) {
		arc4 -> fac = concave_fac;
	}
}
Example #5
0
void LineBuilder::build() {

	// Need at least 2 points to draw a line
	if (points.size() < 2) {
		clear_output();
		return;
	}

	const float hw = width / 2.f;
	const float hw_sq = hw * hw;
	const float sharp_limit_sq = sharp_limit * sharp_limit;
	const int len = points.size();

	// Initial values

	Vector2 pos0 = points[0];
	Vector2 pos1 = points[1];
	Vector2 f0 = (pos1 - pos0).normalized();
	Vector2 u0 = rotate90(f0);
	Vector2 pos_up0 = pos0 + u0 * hw;
	Vector2 pos_down0 = pos0 - u0 * hw;

	Color color0;
	Color color1;

	float current_distance0 = 0.f;
	float current_distance1 = 0.f;
	float total_distance;
	_interpolate_color = gradient != NULL;
	bool distance_required = _interpolate_color || texture_mode == LINE_TEXTURE_TILE;
	if (distance_required)
		total_distance = calculate_total_distance(points);
	if (_interpolate_color)
		color0 = gradient->get_color(0);
	else
		colors.push_back(default_color);

	float uvx0 = 0.f;
	float uvx1 = 0.f;

	// Begin cap
	if (begin_cap_mode == LINE_CAP_BOX) {
		// Push back first vertices a little bit
		pos_up0 -= f0 * hw;
		pos_down0 -= f0 * hw;
		// The line's outer length will be a little higher due to begin and end caps
		total_distance += width;
		current_distance0 += hw;
		current_distance1 = current_distance0;
	} else if (begin_cap_mode == LINE_CAP_ROUND) {
		if (texture_mode == LINE_TEXTURE_TILE) {
			uvx0 = 0.5f;
		}
		new_arc(pos0, pos_up0 - pos0, -Math_PI, color0, Rect2(0.f, 0.f, 1.f, 1.f));
		total_distance += width;
		current_distance0 += hw;
		current_distance1 = current_distance0;
	}

	strip_begin(pos_up0, pos_down0, color0, uvx0);

	//  pos_up0 ------------- pos_up1 --------------------
	//     |                     |
	//   pos0 - - - - - - - - - pos1 - - - - - - - - - pos2
	//     |                     |
	// pos_down0 ------------ pos_down1 ------------------
	//
	//   i-1                     i                      i+1

	// http://labs.hyperandroid.com/tag/opengl-lines
	// (not the same implementation but visuals help a lot)

	// For each additional segment
	for (int i = 1; i < len - 1; ++i) {

		pos1 = points[i];
		Vector2 pos2 = points[i + 1];

		Vector2 f1 = (pos2 - pos1).normalized();
		Vector2 u1 = rotate90(f1);

		// Determine joint orientation
		const float dp = u0.dot(f1);
		const Orientation orientation = (dp > 0.f ? UP : DOWN);

		Vector2 inner_normal0, inner_normal1;
		if (orientation == UP) {
			inner_normal0 = u0 * hw;
			inner_normal1 = u1 * hw;
		} else {
			inner_normal0 = -u0 * hw;
			inner_normal1 = -u1 * hw;
		}

		// ---------------------------
		//                        /
		// 0                     /    1
		//                      /          /
		// --------------------x------    /
		//                    /          /    (here shown with orientation == DOWN)
		//                   /          /
		//                  /          /
		//                 /          /
		//                     2     /
		//                          /

		// Find inner intersection at the joint
		Vector2 corner_pos_in, corner_pos_out;
		SegmentIntersectionResult intersection_result = segment_intersection(
				pos0 + inner_normal0, pos1 + inner_normal0,
				pos1 + inner_normal1, pos2 + inner_normal1,
				&corner_pos_in);

		if (intersection_result == SEGMENT_INTERSECT)
			// Inner parts of the segments intersect
			corner_pos_out = 2.f * pos1 - corner_pos_in;
		else {
			// No intersection, segments are either parallel or too sharp
			corner_pos_in = pos1 + inner_normal0;
			corner_pos_out = pos1 - inner_normal0;
		}

		Vector2 corner_pos_up, corner_pos_down;
		if (orientation == UP) {
			corner_pos_up = corner_pos_in;
			corner_pos_down = corner_pos_out;
		} else {
			corner_pos_up = corner_pos_out;
			corner_pos_down = corner_pos_in;
		}

		LineJointMode current_joint_mode = joint_mode;

		Vector2 pos_up1, pos_down1;
		if (intersection_result == SEGMENT_INTERSECT) {
			// Fallback on bevel if sharp angle is too high (because it would produce very long miters)
			if (current_joint_mode == LINE_JOINT_SHARP && corner_pos_out.distance_squared_to(pos1) / hw_sq > sharp_limit_sq) {
				current_joint_mode = LINE_JOINT_BEVEL;
			}
			if (current_joint_mode == LINE_JOINT_SHARP) {
				// In this case, we won't create joint geometry,
				// The previous and next line quads will directly share an edge.
				pos_up1 = corner_pos_up;
				pos_down1 = corner_pos_down;
			} else {
				// Bevel or round
				if (orientation == UP) {
					pos_up1 = corner_pos_up;
					pos_down1 = pos1 - u0 * hw;
				} else {
					pos_up1 = pos1 + u0 * hw;
					pos_down1 = corner_pos_down;
				}
			}
		} else {
			// No intersection: fallback
			pos_up1 = corner_pos_up;
			pos_down1 = corner_pos_down;
		}

		// Add current line body quad
		// Triangles are clockwise
		if (distance_required) {
			current_distance1 += pos0.distance_to(pos1);
		}
		if (_interpolate_color) {
			color1 = gradient->get_color_at_offset(current_distance1 / total_distance);
		}
		if (texture_mode == LINE_TEXTURE_TILE) {
			uvx0 = current_distance0 / width;
			uvx1 = current_distance1 / width;
		}

		strip_add_quad(pos_up1, pos_down1, color1, uvx1);

		// Swap vars for use in the next line
		color0 = color1;
		u0 = u1;
		f0 = f1;
		pos0 = pos1;
		current_distance0 = current_distance1;
		if (intersection_result == SEGMENT_INTERSECT) {
			if (current_joint_mode == LINE_JOINT_SHARP) {
				pos_up0 = pos_up1;
				pos_down0 = pos_down1;
			} else {
				if (orientation == UP) {
					pos_up0 = corner_pos_up;
					pos_down0 = pos1 - u1 * hw;
				} else {
					pos_up0 = pos1 + u1 * hw;
					pos_down0 = corner_pos_down;
				}
			}
		} else {
			pos_up0 = pos1 + u1 * hw;
			pos_down0 = pos1 - u1 * hw;
		}
		// From this point, bu0 and bd0 concern the next segment

		// Add joint geometry
		if (current_joint_mode != LINE_JOINT_SHARP) {

			// ________________ cbegin
			//               / \
			//              /   \
			// ____________/_ _ _\ cend
			//             |     |
			//             |     |
			//             |     |

			Vector2 cbegin, cend;
			if (orientation == UP) {
				cbegin = pos_down1;
				cend = pos_down0;
			} else {
				cbegin = pos_up1;
				cend = pos_up0;
			}

			if (current_joint_mode == LINE_JOINT_BEVEL) {
				strip_add_tri(cend, orientation);
			} else if (current_joint_mode == LINE_JOINT_ROUND) {
				Vector2 vbegin = cbegin - pos1;
				Vector2 vend = cend - pos1;
				strip_add_arc(pos1, vend.angle_to(vbegin), orientation);
			}

			if (intersection_result != SEGMENT_INTERSECT)
				// In this case the joint is too f****d up to be re-used,
				// start again the strip with fallback points
				strip_begin(pos_up0, pos_down0, color1, uvx1);
		}
	}

	// Last (or only) segment

	pos1 = points[points.size() - 1];

	Vector2 pos_up1 = pos1 + u0 * hw;
	Vector2 pos_down1 = pos1 - u0 * hw;

	// End cap (box)
	if (end_cap_mode == LINE_CAP_BOX) {
		pos_up1 += f0 * hw;
		pos_down1 += f0 * hw;
	}

	if (distance_required) {
		current_distance1 += pos0.distance_to(pos1);
	}
	if (_interpolate_color) {
		color1 = gradient->get_color(gradient->get_points_count() - 1);
	}
	if (texture_mode == LINE_TEXTURE_TILE) {
		uvx1 = current_distance1 / width;
	}

	strip_add_quad(pos_up1, pos_down1, color1, uvx1);

	// End cap (round)
	if (end_cap_mode == LINE_CAP_ROUND) {
		// Note: color is not used in case we don't interpolate...
		Color color = _interpolate_color ? gradient->get_color(gradient->get_points_count() - 1) : Color(0, 0, 0);
		new_arc(pos1, pos_up1 - pos1, Math_PI, color, Rect2(uvx1 - 0.5f, 0.f, 1.f, 1.f));
	}
}