Rectangle GeometricRecognizer::boundingBox(Path2D points) { double minX = MAX_DOUBLE; double maxX = -MAX_DOUBLE; double minY = MAX_DOUBLE; double maxY = -MAX_DOUBLE; for (Path2DIterator i = points.begin(); i != points.end(); i++) { Point2D point = *i; if (point.x < minX) minX = point.x; if (point.x > maxX) maxX = point.x; if (point.y < minY) minY = point.y; if (point.y > maxY) maxY = point.y; } Rectangle bounds(minX, minY, (maxX - minX), (maxY - minY)); return bounds; }
//use 40 point Path2D CTemplateRecorder::OutputRectange() { Path2D path; path.resize(40); int curIndex = 0; //left edge //four points //left bottom 0,0 //left top 0,100 //right top 100,100 //right bottom 100,0 for(int i=0;i<10;++i) //left bottom to left top { path[curIndex].x = 0.0f; path[curIndex].y = i*10.0f; curIndex ++; } for(int i=0;i<10;++i) //left top to right top { path[curIndex].x = i*10.0f; path[curIndex].y = 100.0f; curIndex ++; } for(int i=0;i<10;++i) //right top to right bottom { path[curIndex].x = 100.0f; path[curIndex].y = 100.0f - i*10.0f; curIndex ++; } for(int i=0;i<10;++i) { path[curIndex].x = 100.0f - i*10.0f; path[curIndex].y = 0.0f; curIndex ++; } return path; }
std::vector<Vector2f> GeneratePathNormals(const Path2D& path, bool is_closed, bool outward) { assert(path.size() >= 2); // Here we are going to use a simple 3-point interpolation method // first we need to make the path in ccw direction ClipperLib::Path scaled_path = UScalePathDiaToClipper(path); if (!ClipperLib::Orientation(scaled_path)) { ClipperLib::ReversePath(scaled_path); } Path2D ccw_path = DScalePathClipperToDia(scaled_path); // Special treatment for the first and last point. std::vector<Vector2f> output_vecs(path.size()); if (is_closed) { output_vecs[0] = GetNormalFromTwoPoints(ccw_path[ccw_path.size() - 1], ccw_path[1]); output_vecs.back() = GetNormalFromTwoPoints(ccw_path[ccw_path.size() - 2], ccw_path[0]); } else { output_vecs[0] = GetNormalFromTwoPoints(ccw_path[0], ccw_path[1]); output_vecs.back() = GetNormalFromTwoPoints(ccw_path[ccw_path.size() - 2], ccw_path[ccw_path.size() - 1]); } // We use the point before and after ith point to interpolate its normal. for (size_t i = 1; i < ccw_path.size() - 1; ++i) { output_vecs[i] = GetNormalFromTwoPoints(ccw_path[i - 1], ccw_path[i + 1]); } // Reverse normal direction if (!outward) { for (size_t i = 0; i < ccw_path.size(); ++i) { output_vecs[i] = -output_vecs[i]; } } return output_vecs; }
void Shape2D::add_ellipse(const Pointf ¢er, const Pointf &radius, bool reverse) { float offset_x = 0; float offset_y = 0; int max_radius = max(radius.x, radius.y); int rotationcount = max(5, (max_radius - 3)); float halfpi = 1.5707963267948966192313216916398f; float turn = halfpi / rotationcount; offset_x = center.x; offset_y = -center.y; Path2D path; rotationcount *= 4; std::vector<Pointf> points; points.resize(rotationcount); for(int i = 0; i < rotationcount ; i++) { float pos1 = radius.x * cos(i * turn); float pos2 = radius.y * sin(i * turn); points[i].x = (center.x + pos1); points[i].y = (center.y + pos2); } path.add_line_to(points); if (reverse) path.reverse(); add_path(path); }
Path2D Path2D::circle( CGFloat r, const CGPoint &i_center ) { Path2D p; r = std::abs( r ); CGFloat c = r * 0.551915024494; p.move_to( i_center + CGPoint{ r, 0 } ); p.curve_to( i_center + CGPoint{ r, c }, i_center + CGPoint{ c, r }, i_center + CGPoint{ 0, r } ); p.curve_to( i_center + CGPoint{ -c, r }, i_center + CGPoint{ -r, c }, i_center + CGPoint{ -r, 0 } ); p.curve_to( i_center + CGPoint{ -r, -c }, i_center + CGPoint{ -c, -r }, i_center + CGPoint{ 0, -r } ); p.curve_to( i_center + CGPoint{ c, -r }, i_center + CGPoint{ r, -c }, i_center + CGPoint{ r, 0 } ); p.close(); return p; }
void Shape2D_Impl::add_rotated_curve(Path2D &path, const Pointf ¢er, const Angle &angle, Pointf point_1, Pointf point_2, Pointf point_3) { if (angle.to_radians() != 0.0f) { point_1.rotate(center, angle); point_2.rotate(center, angle); point_3.rotate(center, angle); } BezierCurve curve; curve.add_control_point(point_1); curve.add_control_point(point_2); curve.add_control_point(point_3); path.add_curve(curve); }
vector<double> GeometricRecognizer::vectorize(Path2D points) // for Protractor { double sum = 0.0; vector<double> vectorized; // Preprocessing, Move from .x.y notation to 1D vector notation // points[i](.x,.y) => vectorized(i, i+1, ...) for (unsigned int i = 0; i < points.size(); i++) { vectorized.push_back(points[i].x); vectorized.push_back(points[i].y); sum += points[i].x * points[i].x + points[i].y * points[i].y; } // normalize values : dividing by magnitude // magnitude = sqrt ( sum_i(x²) + sum_i(y²) ) double magnitude = sqrt(sum); for (unsigned int i = 0; i < vectorized.size(); i++) vectorized[i] /= magnitude; return vectorized; }
std::vector<Vector2f> SimplifyPolyline(const Path2D& path, float rel_tol) { if (path.size() <= 2) { return path; } std::vector<Vector2f> out; if (true) { // we compute the boundary square size of the path AABB bound = GetAABBWithPadding(path, 0); Vector2f span = bound.upper_bound - bound.lower_bound; // set a tolerance to remove points that are too close to // each other. the relative tolerance is 0.5% of max size // so the simplified curve is still pretty smooth float tol = std::max(span(0), span(1)) * rel_tol; out = PolylineDouglasPeuckerIterative(path, tol); } else { // only one method implemented // the douglas_peucker method may change the topology of input curves assert(0); } return out; }
Shape2D FontEngine_Freetype::load_glyph_outline(int c, int &out_advance_x) { out_advance_x = 0; FT_UInt glyph_index; glyph_index = FT_Get_Char_Index( face, FT_ULong(c) ); FT_Error error = FT_Load_Glyph( face, glyph_index, FT_LOAD_DEFAULT ); if ( error ) { throw Exception("freetype: error loading glyph"); } FT_Glyph glyph; error = FT_Get_Glyph( face->glyph, &glyph ); if ( error ) { throw Exception("freetype: error getting glyph"); } FT_OutlineGlyph ft_outline_glyph_rec = (FT_OutlineGlyph)glyph; FT_Outline ft_outline = ft_outline_glyph_rec->outline; Shape2D outline; // cl_write_console_line(string_format("Num contours: %1", ft_outline.n_contours)); for( int cont = 0; cont < ft_outline.n_contours; cont++ ) { // cl_write_console_line(string_format("Num points in contour %1: %2", cont, ft_outline.contours[0]+1)); Path2D contour; // debug: dump contents of points array to terminal // for( int i = 0; i <= ft_outline.contours[cont]; ++i ) // { // FT_Vector pos = ft_outline.points[i]; // cl_write_console_line(string_format("dump points[%1]: (%2,%3) \t type: %4", i, pos.x, pos.y, ft_outline.tags[i])); // } std::vector<TaggedPoint> points = get_contour_points(cont, &ft_outline); points.push_back(points.front()); // just to simplify, it's removed later. for( unsigned int i = 0; i < points.size()-1; i++ ) { TaggedPoint &tp = points[i]; if( tp.tag == FT_Curve_Tag_On ) { contour.add_line_to(tp.pos); } else if( tp.tag == FT_Curve_Tag_Conic ) { // TODO: i - 1 is safe here because we made sure the contour will start with a Tag_On. BezierCurve curve; curve.add_control_point( points[i-1].pos); curve.add_control_point( tp.pos ); curve.add_control_point( points[i+1].pos ); contour.add_curve(curve); } else if( tp.tag == FT_Curve_Tag_Cubic && points[i-1].tag == FT_Curve_Tag_Cubic ) { BezierCurve curve; curve.add_control_point( points[i-2].pos); curve.add_control_point( points[i-1].pos); curve.add_control_point( tp.pos ); curve.add_control_point( points[i+1].pos ); contour.add_curve(curve); } } outline.add_path(contour); } FT_Done_Glyph(glyph); out_advance_x = get_advance_x( c ); return outline; }
Path2D GeometricRecognizer::resample(Path2D points) { double interval = pathLength(points) / (numPointsInGesture - 1); // interval length double D = 0.0; Path2D newPoints; if (!points.empty()){ //--- Store first point since we'll never resample it out of existence newPoints.push_back(points.front()); for(int i = 1; i < (int)points.size(); i++) { Point2D currentPoint = points[i]; Point2D previousPoint = points[i-1]; double d = getDistance(previousPoint, currentPoint); if ((D + d) >= interval) { double qx = previousPoint.x + ((interval - D) / d) * (currentPoint.x - previousPoint.x); double qy = previousPoint.y + ((interval - D) / d) * (currentPoint.y - previousPoint.y); Point2D point(qx, qy); newPoints.push_back(point); points.insert(points.begin() + i, point); D = 0.0; } else D += d; } // somtimes we fall a rounding-error short of adding the last point, so add it if so if (newPoints.size() == (numPointsInGesture - 1)) { newPoints.push_back(points.back()); } } return newPoints; }