Reading SVG into a path

If you have a cool piece of software to share, but you are not hosting it officially yet, please dump it in here. If you have code snippets that are useful, please donate!
Post Reply
beneficii
Earned some good credits
Earned some good credits
Posts: 111
Joined: Fri Nov 27, 2009 2:49 am

Reading SVG into a path

Post by beneficii »

Z/multiple M/and arcs are not supported, but this otherwise loads an SVG path:

Code: Select all

void read_svg(wxString path, fill_path& fpaths) {
	bool not_started_flag = true, last_cubic = false, last_quad = false;
	for (std::size_t i = 0; i < path.Length; ++i) {
		switch (static_cast<char>(path[i])) {
		case 'M':
		case 'm':
		case 'V':
		case 'v':
		case 'H':
		case 'h':
		case 'L':
		case 'l':
		case 'q':
		case 'Q':
		case 't':
		case 'T':
		case 'c':
		case 'C':
		case 's':
		case 'S':
		case 'a':
		case 'A':
			path.insert(i + 1, " ");
		}
	}
	wxStringTokenizer tkn(path, ", \t\r\n", wxTOKEN_STRTOK);
	wxPoint2DDouble P0(0.0, 0.0);
	wxPoint2DDouble P1(0.0, 0.0);
	wxPoint2DDouble P2(0.0, 0.0);
	wxPoint2DDouble P3(0.0, 0.0);
	wxPoint2DDouble Q1(0.0, 0.0);
	wxPoint2DDouble Q2(0.0, 0.0);
	while (tkn.HasMoreTokens()) {
		bool rel = false;
		wxString get_tok = tkn.NextToken();
		switch (static_cast<char>(get_tok[0])) {
		case 'Z':  //z not supported
		case 'z':
			return;
		case 'M':
		case 'm':
			if (not_started_flag) {
				not_started_flag = false;
				tkn.NextToken().ToCDouble(&static_cast<double>(P0.m_x));
				tkn.NextToken().ToCDouble(&static_cast<double>(P0.m_y));
			}
			else return;  //multiple m's not supported
			break;
		case 'v':
			rel = true;
		case 'V':
			P3.m_x = P0.m_x;
			tkn.NextToken().ToCDouble(&static_cast<double>(P3.m_y));
			if (rel) {
				P3.m_y += P0.m_y;
			}
			P1 = P0;
			P2 = P3;
			fpaths.insert_line(P0, P3);
			P0 = P3;
			last_cubic = false;
			last_quad = false;
			break;
		case 'h':
			rel = true;
		case 'H':
			tkn.NextToken().ToCDouble(&static_cast<double>(P3.m_x));
			P3.m_y = P0.m_y;
			if (rel) {
				P3.m_x += P0.m_x;
			}
			P1 = P0;
			P2 = P3;
			fpaths.insert_line(P0, P3);
			P0 = P3;
			last_cubic = false;
			last_quad = false;
			break;
		case 'l':
			rel = true;
		case 'L':
			tkn.NextToken().ToCDouble(&static_cast<double>(P3.m_x));
			tkn.NextToken().ToCDouble(&static_cast<double>(P3.m_y));
			if (rel) {
				P3.m_x += P0.m_x;
				P3.m_y += P0.m_y;
			}
			P1 = P0;
			P2 = P3;
			fpaths.insert_line(P0, P3);
			P0 = P3;
			last_cubic = false;
			last_quad = false;
			break;
		case 'q':
			rel = true;
		case 'Q': 
			tkn.NextToken().ToCDouble(&static_cast<double>(Q1.m_x));
			tkn.NextToken().ToCDouble(&static_cast<double>(Q1.m_y));
			tkn.NextToken().ToCDouble(&static_cast<double>(Q2.m_x));
			tkn.NextToken().ToCDouble(&static_cast<double>(Q2.m_y));
			if (rel) {
				Q1.m_x += P0.m_x;
				Q1.m_y += P0.m_y;
				Q2.m_x += P0.m_x;
				Q2.m_y += P0.m_y;
			}
			//degree elevation here
			P1.m_x = (P0.m_x + 2.0 * Q1.m_x) / 3.0;
			P1.m_y = (P0.m_y + 2.0 * Q1.m_y) / 3.0;
			P2.m_x = (2.0 * Q1.m_x + Q2.m_x) / 3.0;
			P2.m_y = (2.0 * Q1.m_y + Q2.m_y) / 3.0;
			P3.m_x = Q2.m_x;
			P3.m_y = Q2.m_y;
			fpaths.insert_path(P0, P1, P2, P3);
			P0 = P3;
			last_cubic = false;
			last_quad = true;
			break;
		case 't':
			rel = true;
		case 'T':
			if (!last_quad) return;
 			Q1.m_x = 2.0 * Q2.m_x - Q1.m_x;
			Q1.m_y = 2.0 * Q2.m_x - Q1.m_x;
			tkn.NextToken().ToCDouble(&static_cast<double>(Q2.m_x));
			tkn.NextToken().ToCDouble(&static_cast<double>(Q2.m_y));
			if (rel) {
				Q2.m_x += P0.m_x;
				Q2.m_y += P0.m_y;
			}
			//degree elevation here
			P1.m_x = (P0.m_x + 2.0 * Q1.m_x) / 3.0;
			P1.m_y = (P0.m_y + 2.0 * Q1.m_y) / 3.0;
			P2.m_x = (2.0 * Q1.m_x + Q2.m_x) / 3.0;
			P2.m_y = (2.0 * Q1.m_y + Q2.m_y) / 3.0;
			P3.m_x = Q2.m_x;
			P3.m_y = Q2.m_y;
			fpaths.insert_path(P0, P1, P2, P3);
			P0 = P3;
			break;
		case 'c':
			rel = true;
		case 'C':
			tkn.NextToken().ToCDouble(&static_cast<double>(P1.m_x));
			tkn.NextToken().ToCDouble(&static_cast<double>(P1.m_y));
			tkn.NextToken().ToCDouble(&static_cast<double>(P2.m_x));
			tkn.NextToken().ToCDouble(&static_cast<double>(P2.m_y));
			tkn.NextToken().ToCDouble(&static_cast<double>(P3.m_x));
			tkn.NextToken().ToCDouble(&static_cast<double>(P3.m_y));
			if (rel) {
				P1.m_x += P0.m_x;
				P1.m_y += P0.m_y;
				P2.m_x += P0.m_x;
				P2.m_y += P0.m_y;
				P3.m_x += P0.m_x;
				P3.m_y += P0.m_y;
			}
			fpaths.insert_path(P0, P1, P2, P3);
			P0 = P3;
			last_cubic = true;
			last_quad = false;
			break;
		case 's':
			rel = true;
		case 'S':
			if (!last_cubic) return;
			tkn.NextToken().ToCDouble(&static_cast<double>(P2.m_x));
			tkn.NextToken().ToCDouble(&static_cast<double>(P2.m_y));
			tkn.NextToken().ToCDouble(&static_cast<double>(P3.m_x));
			tkn.NextToken().ToCDouble(&static_cast<double>(P3.m_y));
			if (rel) {
				P2.m_x += P0.m_x;
				P2.m_y += P0.m_y;
				P3.m_x += P0.m_x;
				P3.m_y += P0.m_y;
			}
			fpaths.insert_path(P2, P3);
			P0 = P3;
			break;
		default:
			return;  //arcs not supported
		}
	}
}
This code will then take the path and work magic on it, by creating offsets, including changing offsets, and creating things like roads with lanes that expand and contract.
Post Reply