Page 1 of 1

Reading SVG into a path

Posted: Thu Aug 07, 2014 7:49 am
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.