/*! */ void ConvexHull::computeGrahamScan() { clearResults(); const size_t point_size = M_input_points.size(); if ( point_size < 3 ) { return; } size_t min_index = getMinPointIndex(); if ( min_index == size_t( -1 ) ) { return; } sortPointsByAngleFrom( min_index ); M_vertices = M_input_points; size_t top = 1; for ( size_t i = 2; i < point_size; ++i ) { while ( is_clockwise( M_vertices[top-1], M_vertices[top], M_input_points[i] ) ) { --top; } ++top; std::swap( M_vertices[top], M_vertices[i] ); } ++top; M_vertices.erase( M_vertices.begin() + top, M_vertices.end() ); VertexCont::iterator p = M_vertices.begin(); VertexCont::iterator n = p; ++n; for ( ; n != M_vertices.end(); ++n ) { M_edges.push_back( Segment2D( *p, *n ) ); p = n; } M_edges.push_back( Segment2D( M_vertices.back(), M_vertices.front() ) ); }
static void BluntJoiner(SkPath* outer, SkPath* inner, const SkVector& beforeUnitNormal, const SkPoint& pivot, const SkVector& afterUnitNormal, SkScalar radius, SkScalar invMiterLimit, bool, bool) { SkVector after; afterUnitNormal.scale(radius, &after); if (!is_clockwise(beforeUnitNormal, afterUnitNormal)) { SkTSwap<SkPath*>(outer, inner); after.negate(); } outer->lineTo(pivot.fX + after.fX, pivot.fY + after.fY); HandleInnerJoin(inner, pivot, after); }
void read_polycont(const std::string& filename, ContContainer& polycont) { /* Open filestream */ std::fstream fs(filename, std::fstream::in); /* Get size */ std::string sx, sy; std::getline(fs, sx, ','); std::getline(fs, sy, '/'); /* 'Allocate' */ polycont.im_sx = std::stoi(sx); polycont.im_sy = std::stoi(sy); /* Separate the object into blobs */ std::string path_str; while(std::getline(fs, path_str, '/')) { std::istringstream ss(path_str); std::string coord_str; bool is_x = true; Path path; Point pt; while(std::getline(ss, coord_str, ',')) { if (is_x) pt.X = std::stoi(coord_str); else { pt.Y = std::stoi(coord_str); path.push_back(pt); } is_x = !is_x; } polycont.push_back(path); } /* Fill in the hole indicator */ polycont.is_hole.resize(polycont.size()); for(std::size_t ii=0; ii<polycont.size(); ++ii) polycont.is_hole[ii] = !is_clockwise(polycont[ii]); }
static void RoundJoiner(SkPath* outer, SkPath* inner, const SkVector& beforeUnitNormal, const SkPoint& pivot, const SkVector& afterUnitNormal, SkScalar radius, SkScalar invMiterLimit, bool, bool) { SkScalar dotProd = SkPoint::DotProduct(beforeUnitNormal, afterUnitNormal); AngleType angleType = Dot2AngleType(dotProd); if (angleType == kNearlyLine_AngleType) return; SkVector before = beforeUnitNormal; SkVector after = afterUnitNormal; SkRotationDirection dir = kCW_SkRotationDirection; if (!is_clockwise(before, after)) { SkTSwap<SkPath*>(outer, inner); before.negate(); after.negate(); dir = kCCW_SkRotationDirection; } SkPoint pts[kSkBuildQuadArcStorage]; SkMatrix matrix; matrix.setScale(radius, radius); matrix.postTranslate(pivot.fX, pivot.fY); int count = SkBuildQuadArc(before, after, dir, &matrix, pts); SkASSERT((count & 1) == 1); if (count > 1) { for (int i = 1; i < count; i += 2) outer->quadTo(pts[i].fX, pts[i].fY, pts[i+1].fX, pts[i+1].fY); after.scale(radius); HandleInnerJoin(inner, pivot, after); } }
static void MiterJoiner(SkPath* outer, SkPath* inner, const SkVector& beforeUnitNormal, const SkPoint& pivot, const SkVector& afterUnitNormal, SkScalar radius, SkScalar invMiterLimit, bool prevIsLine, bool currIsLine) { // negate the dot since we're using normals instead of tangents SkScalar dotProd = SkPoint::DotProduct(beforeUnitNormal, afterUnitNormal); AngleType angleType = Dot2AngleType(dotProd); SkVector before = beforeUnitNormal; SkVector after = afterUnitNormal; SkVector mid; SkScalar sinHalfAngle; bool ccw; if (angleType == kNearlyLine_AngleType) return; if (angleType == kNearly180_AngleType) { currIsLine = false; goto DO_BLUNT; } ccw = !is_clockwise(before, after); if (ccw) { SkTSwap<SkPath*>(outer, inner); before.negate(); after.negate(); } /* Before we enter the world of square-roots and divides, check if we're trying to join an upright right angle (common case for stroking rectangles). If so, special case that (for speed an accuracy). Note: we only need to check one normal if dot==0 */ if (0 == dotProd && invMiterLimit <= kOneOverSqrt2) { mid.set(SkScalarMul(before.fX + after.fX, radius), SkScalarMul(before.fY + after.fY, radius)); goto DO_MITER; } /* midLength = radius / sinHalfAngle if (midLength > miterLimit * radius) abort if (radius / sinHalf > miterLimit * radius) abort if (1 / sinHalf > miterLimit) abort if (1 / miterLimit > sinHalf) abort My dotProd is opposite sign, since it is built from normals and not tangents hence 1 + dot instead of 1 - dot in the formula */ sinHalfAngle = SkScalarSqrt(SkScalarHalf(SK_Scalar1 + dotProd)); if (sinHalfAngle < invMiterLimit) { currIsLine = false; goto DO_BLUNT; } // choose the most accurate way to form the initial mid-vector if (angleType == kSharp_AngleType) { mid.set(after.fY - before.fY, before.fX - after.fX); if (ccw) mid.negate(); } else mid.set(before.fX + after.fX, before.fY + after.fY); mid.setLength(SkScalarDiv(radius, sinHalfAngle)); DO_MITER: if (prevIsLine) outer->setLastPt(pivot.fX + mid.fX, pivot.fY + mid.fY); else outer->lineTo(pivot.fX + mid.fX, pivot.fY + mid.fY); DO_BLUNT: after.scale(radius); if (!currIsLine) outer->lineTo(pivot.fX + after.fX, pivot.fY + after.fY); HandleInnerJoin(inner, pivot, after); }
bool GrStrokePathRenderer::onDrawPath(const SkPath& origPath, const SkStrokeRec& stroke, GrDrawTarget* target, bool antiAlias) { if (origPath.isEmpty()) { return true; } SkScalar width = stroke.getWidth(); if (width <= 0) { return false; } // Get the join type SkPaint::Join join = stroke.getJoin(); SkScalar miterLimit = stroke.getMiter(); SkScalar sqMiterLimit = SkScalarMul(miterLimit, miterLimit); if ((join == SkPaint::kMiter_Join) && (miterLimit <= SK_Scalar1)) { // If the miter limit is small, treat it as a bevel join join = SkPaint::kBevel_Join; } const bool isMiter = (join == SkPaint::kMiter_Join); const bool isBevel = (join == SkPaint::kBevel_Join); SkScalar invMiterLimit = isMiter ? SK_Scalar1 / miterLimit : 0; SkScalar invMiterLimitSq = SkScalarMul(invMiterLimit, invMiterLimit); // Allocate vertices const int nbQuads = origPath.countPoints() + 1; // Could be "-1" if path is not closed const int extraVerts = isMiter || isBevel ? 1 : 0; const int maxVertexCount = nbQuads * (4 + extraVerts); const int maxIndexCount = nbQuads * (6 + extraVerts * 3); // Each extra vert adds a triangle target->drawState()->setDefaultVertexAttribs(); GrDrawTarget::AutoReleaseGeometry arg(target, maxVertexCount, maxIndexCount); if (!arg.succeeded()) { return false; } SkPoint* verts = reinterpret_cast<SkPoint*>(arg.vertices()); uint16_t* idxs = reinterpret_cast<uint16_t*>(arg.indices()); int vCount = 0, iCount = 0; // Transform the path into a list of triangles SkPath::Iter iter(origPath, false); SkPoint pts[4]; const SkScalar radius = SkScalarMul(width, 0.5f); SkPoint *firstPt = verts, *lastPt = NULL; SkVector firstDir, dir; firstDir.set(0, 0); dir.set(0, 0); bool isOpen = true; for(SkPath::Verb v = iter.next(pts); v != SkPath::kDone_Verb; v = iter.next(pts)) { switch(v) { case SkPath::kMove_Verb: // This will already be handled as pts[0] of the 1st line break; case SkPath::kClose_Verb: isOpen = (lastPt == NULL); break; case SkPath::kLine_Verb: { SkVector v0 = dir; dir = pts[1] - pts[0]; if (dir.setLength(radius)) { SkVector dirT; dirT.set(dir.fY, -dir.fX); // Get perpendicular direction SkPoint l1a = pts[0]+dirT, l1b = pts[1]+dirT, l2a = pts[0]-dirT, l2b = pts[1]-dirT; SkPoint miterPt[2]; bool useMiterPoint = false; int idx0(-1), idx1(-1); if (NULL == lastPt) { firstDir = dir; } else { SkVector v1 = dir; if (v0.normalize() && v1.normalize()) { SkScalar dotProd = v0.dot(v1); // No need for bevel or miter join if the angle // is either 0 or 180 degrees if (!SkScalarNearlyZero(dotProd + SK_Scalar1) && !SkScalarNearlyZero(dotProd - SK_Scalar1)) { bool ccw = !is_clockwise(v0, v1); int offset = ccw ? 1 : 0; idx0 = vCount-2+offset; idx1 = vCount+offset; const SkPoint* pt0 = &(lastPt[offset]); const SkPoint* pt1 = ccw ? &l2a : &l1a; switch(join) { case SkPaint::kMiter_Join: { // *Note : Logic is from MiterJoiner // FIXME : Special case if we have a right angle ? // if (SkScalarNearlyZero(dotProd)) {...} SkScalar sinHalfAngleSq = SkScalarHalf(SK_Scalar1 + dotProd); if (sinHalfAngleSq >= invMiterLimitSq) { // Find the miter point (or points if it is further // than the miter limit) const SkPoint pt2 = *pt0+v0, pt3 = *pt1+v1; if (intersection(*pt0, pt2, *pt1, pt3, miterPt[0]) != kNone_IntersectionType) { SkPoint miterPt0 = miterPt[0] - *pt0; SkPoint miterPt1 = miterPt[0] - *pt1; SkScalar sqDist0 = miterPt0.dot(miterPt0); SkScalar sqDist1 = miterPt1.dot(miterPt1); const SkScalar rSq = SkScalarDiv(SkScalarMul(radius, radius), sinHalfAngleSq); const SkScalar sqRLimit = SkScalarMul(sqMiterLimit, rSq); if (sqDist0 > sqRLimit || sqDist1 > sqRLimit) { if (sqDist1 > sqRLimit) { v1.setLength(SkScalarSqrt(sqRLimit)); miterPt[1] = *pt1+v1; } else { miterPt[1] = miterPt[0]; } if (sqDist0 > sqRLimit) { v0.setLength(SkScalarSqrt(sqRLimit)); miterPt[0] = *pt0+v0; } } else { miterPt[1] = miterPt[0]; } useMiterPoint = true; } } if (useMiterPoint && (miterPt[1] == miterPt[0])) { break; } } default: case SkPaint::kBevel_Join: { // Note : This currently causes some overdraw where both // lines initially intersect. We'd need to add // another line intersection check here if the // overdraw becomes an issue instead of using the // current point directly. // Add center point *verts++ = pts[0]; // Use current point directly // This idx is passed the current point so increment it ++idx1; // Add center triangle *idxs++ = idx0; *idxs++ = vCount; *idxs++ = idx1; vCount++; iCount += 3; } break; } } } } *verts++ = l1a; *verts++ = l2a; lastPt = verts; *verts++ = l1b; *verts++ = l2b; if (useMiterPoint && (idx0 >= 0) && (idx1 >= 0)) { firstPt[idx0] = miterPt[0]; firstPt[idx1] = miterPt[1]; } // 1st triangle *idxs++ = vCount+0; *idxs++ = vCount+2; *idxs++ = vCount+1; // 2nd triangle *idxs++ = vCount+1; *idxs++ = vCount+2; *idxs++ = vCount+3; vCount += 4; iCount += 6; } } break; case SkPath::kQuad_Verb: case SkPath::kCubic_Verb: SkDEBUGFAIL("Curves not supported!"); default: // Unhandled cases SkASSERT(false); } } if (isOpen) { // Add caps switch (stroke.getCap()) { case SkPaint::kSquare_Cap: firstPt[0] -= firstDir; firstPt[1] -= firstDir; lastPt [0] += dir; lastPt [1] += dir; break; case SkPaint::kRound_Cap: SkDEBUGFAIL("Round caps not supported!"); default: // No cap break; } } SkASSERT(vCount <= maxVertexCount); SkASSERT(iCount <= maxIndexCount); if (vCount > 0) { target->drawIndexed(kTriangles_GrPrimitiveType, 0, // start vertex 0, // start index vCount, iCount); } return true; }
/* A chaque step, le Joueur doit se déplacer de vitesse unitée de déplacement. Unitée de déplacement après unitées de déplacement, nous déplacons le Joueur, et calculons la distance du Joueur à chaque segment de chaque polygon. Pour ce faire, nous projetons le joueur sur le droite directrice du segment testé. Si la projection se trouve sur le segment, la distance du Joueur à ce dernier est par conséquent égal à sa distance de la projection. Si la projection n'est pas sur le segment, nous calculons de quel coté du segment elle se trouve, et la distance du player est par conséquent Sa distance au sommet du côté ou il se trouve. Si la distance que l'on vient de calculer est inférieure à rayonPlayer, il y a collision. Il faut donc repositionner le Joueur 1 unitée de déplacement avant ( dernière bonne position sans collision) et changer la direction et la norme du player. Nous distingons deux cas: Cas 1 : La collision se fait sur un sommet: Nous projettons la vitesse et la vitesse_restante_a_parcourir du Joueur sur l'axe perpendiculaire au segment d'extrémités [le centre du joueur, le sommet] Cas 2 : la collision se fait sur une arète: Nous projetteons la vitesse et la vitesse_restante_a_parcourir du Joueur sur le segment. Notons que les norme de la vitesse est évidement diminuée dans l'opération. S'il n'y a eu aucune collision avec l'ensemble des polygones, nous considérons donc la nouvelle position du joueur comme sûr, et nous diminuons de 1 unitée de déplacement vitesse_restante_a_parcourir. Nous répétons donc cet algorithme tant que la vitesse_restante_a_parcourir du joueur n'est pas 0 (ou négligeable) Cette méthode permet de gérer plusieurs collisions qui surviennent lors du déplacement d'une seule unitée de déplacement. */ void update_player(Player* player, Map * map) { double debugg=0; double x1 = player->x; /* emplacement initial du joueur en X*/ double y1 = player->y; /* emplacement initial du joueur en Y */ Polygon* poly = NULL; double x2 = player->x + player->v_x; double y2 = player->y + player->v_y; double dist = 0; double a,b,c; int ttest; double x_modif, y_modif; double sens_x, sens_y; double range; double col_x; /* point de collision en X */ double col_y; /* point de collision en Y */ int col_type; int temp; /* variables temporaires pour eventuelles permutations */ int i, j, zz, jpp, jmm; /* Quelques indices */ int cc; player->on_ground = FALSE; player->on_left_wall = FALSE; player->on_right_wall = FALSE; for (zz=0; zz<map->nb_polygons; zz++) { poly = map->polygons[zz].polygon; for (j=0; j<poly->nb_points; j++) { map->polygons[zz].optimisation[j]=0; } } while (TRUE == TRUE) { /* On calcule les valeurs initiales des Variables nécessaires à la gestion de la physique. */ temp = 0; ttest = 0; update_a_modifs ( &sens_x , &sens_y , (x2-x1) , (y2-y1) , &x_modif , &y_modif , &range); /* Pour chaque unité de déplacement à effectuer = vitesse */ for (i=1; i<=range; i++) { /* on teste la collision avec des cercles */ for(cc = 0; cc < map->nb_circles; cc++) { /*Pour chaque cercle */ if (map->circles[cc].visible == TRUE) { /* si le cercle est visible, c.a.d, proche du player, on calcule la distance 'dist' jusqu'au cercle */ dist = sqrt ( (map->circles[cc].origine_x - x1) * (map->circles[cc].origine_x - x1) + (map->circles[cc].origine_y - y1) * (map->circles[cc].origine_y - y1)); printf("dist = %f\n",dist); if (dist > player->rc + map->circles[cc].radius + 2*range + 1) { /* Si le cercle est trop loin, on le désactive */ map->circles[cc].visible = FALSE; } else if (dist < player->rc + map->circles[i].radius) { /* si le cercle est trop près, on le collisionne */ ttest =1; debugg+=1; col_x = ( x1 * player->rc + map->circles[cc].radius * map->circles[cc].origine_x) / ( player->rc + map->circles[cc].radius); col_y = ( y1 * player->rc + map->circles[cc].radius * map->circles[cc].origine_y) / ( player->rc + map->circles[cc].radius); if (debugg>20) {printf("%f\n",debugg);return;} if ((col_y+1)>y1) { if (col_x > (x1 + (player->r * 0.9))) { player->on_right_wall = TRUE; } else if (col_x < (x1 - (player->r * 0.9))) { player->on_left_wall = TRUE; } else { player->on_ground = TRUE; } } /* mémoire pour projeter la vitesse du joueur */ a = sqrt(player->v_x * player->v_x + player->v_y * player-> v_y); b = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1)); projette_vecteur( x1 , y1 , &x2 , &y2 , x1 , y1 , x1 -(col_y-y1) , y1 +(col_x-x1)); c = a/b; /* On projette la vitesse du joueur */ player->v_x = (x2-x1) * c; player->v_y = (y2-y1) * c; } } } /* on teste la collision avec des polygons */ if (ttest==0) for (zz=0; zz<map->nb_polygons; zz++) { poly = map->polygons[zz].polygon; if (map->polygons[zz].visible == TRUE && ttest ==0) for (j=0; j<poly->nb_points; j++) { if (map->polygons[zz].optimisation[j]==0) { jpp = (j+1) % poly->nb_points; jmm = (poly->nb_points+j-1) % poly->nb_points; dist = distance_point_segment ( x1+x_modif , y1+y_modif , poly->point_x[j] , poly->point_y[j] , poly->point_x[jpp], poly->point_y[jpp] , &col_x , &col_y , &col_type); if (dist > player->rc + 2*range + 1) { /* Si la distance au segment est trop faible On desactive le segment pour le reste du step*/ map->polygons[zz].optimisation[j]=1; } else if (dist < player->rc) { /* S'il y a collision */ ttest =1; debugg+=1; if (debugg>20) {printf("%f\n",debugg);return;} if ((col_y+1)>y1) { /* si la collision se situe sous la ceinture du player*/ if (col_x > (x1 + (player->r * 0.9))) { player->on_right_wall = TRUE; } else if (col_x < (x1 - (player->r * 0.9))) { player->on_left_wall = TRUE; } else { player->on_ground = TRUE; } } a = sqrt(player->v_x * player->v_x + player->v_y * player-> v_y); b = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1)); /* on projette notre vitesse sur la surface collisionnée*/ if (col_type == ARRETE) { /*Cas: la collision se fait sur une arète: Nous projetteons la vitesse et la vitesse_restante_a_parcourir du Joueur sur le segment.*/ projette_vecteur( x1 , y1 , &x2 , &y2 , poly->point_x[j] , poly->point_y[j] , poly->point_x[jpp] , poly->point_y[jpp]); } else if (is_clockwise( poly->point_x[jmm], poly->point_y[jmm] , poly->point_x[j] , poly->point_y[j] , poly->point_x[jpp], poly->point_y[jpp]) != CLOCKWISE) { /*Cas: La collision se fait sur un sommet: Nous projettons la vitesse et la vitesse_restante_a_parcourir du Joueur sur l'axe perpendiculaire au segment d'extrémités [le centre du joueur, le sommet] */ projette_vecteur( x1 , y1 , &x2 , &y2 , x1 , y1 , x1 -(col_y-y1) , y1 +(col_x-x1)); } /* et on décellère car de l'énergie est absorbée lors de la collision */ c = a/b; /* On projette la vitesse du joueur */ player->v_x = (x2-x1) * c; player->v_y = (y2-y1) * c; } else if (dist < player->rc+1) { /* on teste si le player est contre un mur */ if ((col_y+1)>y1) { if (col_x > (x1 + (player->r * 0.9))) { player->on_right_wall = TRUE; } else if (col_x < (x1 - (player->r * 0.9))) { player->on_left_wall = TRUE; } else { player->on_ground = TRUE; } } } } } } if (ttest==0) { /* On vient de finir un déplacement d'un pixel */ x1+= x_modif; y1+= y_modif; for(cc = 0; cc < map->nb_pieces; cc++) { /* pour chaque pièce */ if (sqrt( (map->pieces_x[cc] - x1) * (map->pieces_x[cc] - x1) + (map->pieces_y[cc] - y1) * (map->pieces_y[cc] - y1)) < (player->rc + 5) && map->pieces_prises[cc] == 0) { /* si on est assez près et que la pièce n'a pas encore été mangée, On la mange. */ map->pieces_prises[cc] =1; player->pieces ++; } } if (range==i) { /* si on est arrivé à terme de notre déplacement, c'est finit pour ce step. */ player->x= x1; player->y= y1; return; } } } } return ; }
bool ConvexHull::meets_invariants() const { return is_clockwise() && top_point_first() && no_colinear_points(); }