void TransformOvalClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer, wxPoint aStart, wxPoint aEnd, int aWidth, int aCircleToSegmentsCount, double aCorrectionFactor ) { // To build the polygonal shape outside the actual shape, we use a bigger // radius to build rounded ends. // However, the width of the segment is too big. // so, later, we will clamp the polygonal shape with the bounding box // of the segment. int radius = aWidth / 2; // Note if we want to compensate the radius reduction of a circle due to // the segment approx, aCorrectionFactor must be calculated like this: // For a circle the min radius is radius * cos( 2PI / s_CircleToSegmentsCount / 2) // aCorrectionFactor is 1 /cos( PI/s_CircleToSegmentsCount ) radius = radius * aCorrectionFactor; // make segments outside the circles // end point is the coordinate relative to aStart wxPoint endp = aEnd - aStart; wxPoint startp = aStart; wxPoint corner; SHAPE_POLY_SET polyshape; polyshape.NewOutline(); // normalize the position in order to have endp.x >= 0 // it makes calculations more easy to understand if( endp.x < 0 ) { endp = aStart - aEnd; startp = aEnd; } // delta_angle is in radian double delta_angle = atan2( (double)endp.y, (double)endp.x ); int seg_len = KiROUND( EuclideanNorm( endp ) ); double delta = 3600.0 / aCircleToSegmentsCount; // rot angle in 0.1 degree // Compute the outlines of the segment, and creates a polygon // Note: the polygonal shape is built from the equivalent horizontal // segment starting ar 0,0, and ending at seg_len,0 // add right rounded end: for( int ii = 0; ii < aCircleToSegmentsCount/2; ii++ ) { corner = wxPoint( 0, radius ); RotatePoint( &corner, delta*ii ); corner.x += seg_len; polyshape.Append( corner.x, corner.y ); } // Finish arc: corner = wxPoint( seg_len, -radius ); polyshape.Append( corner.x, corner.y ); // add left rounded end: for( int ii = 0; ii < aCircleToSegmentsCount/2; ii++ ) { corner = wxPoint( 0, -radius ); RotatePoint( &corner, delta*ii ); polyshape.Append( corner.x, corner.y ); } // Finish arc: corner = wxPoint( 0, radius ); polyshape.Append( corner.x, corner.y ); // Now, clamp the polygonal shape (too big) with the segment bounding box // the polygonal shape bbox equivalent to the segment has a too big height, // and the right width if( aCorrectionFactor > 1.0 ) { SHAPE_POLY_SET bbox; bbox.NewOutline(); // Build the bbox (a horizontal rectangle). int halfwidth = aWidth / 2; // Use the exact segment width for the bbox height corner.x = -radius - 2; // use a bbox width slightly bigger to avoid // creating useless corner at segment ends corner.y = halfwidth; bbox.Append( corner.x, corner.y ); corner.y = -halfwidth; bbox.Append( corner.x, corner.y ); corner.x = radius + seg_len + 2; bbox.Append( corner.x, corner.y ); corner.y = halfwidth; bbox.Append( corner.x, corner.y ); // Now, clamp the shape polyshape.BooleanIntersection( bbox, SHAPE_POLY_SET::PM_STRICTLY_SIMPLE ); // Note the final polygon is a simple, convex polygon with no hole // due to the shape of initial polygons } // Rotate and move the polygon to its right location polyshape.Rotate( delta_angle, VECTOR2I( 0, 0 ) ); polyshape.Move( startp ); aCornerBuffer.Append( polyshape); }
void TransformRoundChamferedRectToPolygon( SHAPE_POLY_SET& aCornerBuffer, const wxPoint& aPosition, const wxSize& aSize, double aRotation, int aCornerRadius, double aChamferRatio, int aChamferCorners, int aCircleToSegmentsCount ) { // Build the basic shape in orientation 0.0, position 0,0 for chamfered corners // or in actual position/orientation for round rect only wxPoint corners[4]; GetRoundRectCornerCenters( corners, aCornerRadius, aChamferCorners ? wxPoint( 0, 0 ) : aPosition, aSize, aChamferCorners ? 0.0 : aRotation ); SHAPE_POLY_SET outline; outline.NewOutline(); for( int ii = 0; ii < 4; ++ii ) outline.Append( corners[ii].x, corners[ii].y ); outline.Inflate( aCornerRadius, aCircleToSegmentsCount ); if( aChamferCorners == RECT_NO_CHAMFER ) // no chamfer { // Add the outline: aCornerBuffer.Append( outline ); return; } // Now we have the round rect outline, in position 0,0 orientation 0.0. // Chamfer the corner(s). int chamfer_value = aChamferRatio * std::min( aSize.x, aSize.y ); SHAPE_POLY_SET chamfered_corner; // corner shape for the current corner to chamfer int corner_id[4] = { RECT_CHAMFER_TOP_LEFT, RECT_CHAMFER_TOP_RIGHT, RECT_CHAMFER_BOTTOM_LEFT, RECT_CHAMFER_BOTTOM_RIGHT }; // Depending on the corner position, signX[] and signY[] give the sign of chamfer // coordinates relative to the corner position // The first corner is the top left corner, then top right, bottom left and bottom right int signX[4] = {1, -1, 1,-1 }; int signY[4] = {1, 1, -1,-1 }; for( int ii = 0; ii < 4; ii++ ) { if( (corner_id[ii] & aChamferCorners) == 0 ) continue; VECTOR2I corner_pos( -signX[ii]*aSize.x/2, -signY[ii]*aSize.y/2 ); if( aCornerRadius ) { // We recreate a rectangular area covering the full rounded corner (max size = aSize/2) // to rebuild the corner before chamfering, to be sure the rounded corner shape does not // overlap the chamfered corner shape: chamfered_corner.RemoveAllContours(); chamfered_corner.NewOutline(); chamfered_corner.Append( 0, 0 ); chamfered_corner.Append( 0, signY[ii]*aSize.y/2 ); chamfered_corner.Append( signX[ii]*aSize.x/2, signY[ii]*aSize.y/2 ); chamfered_corner.Append( signX[ii]*aSize.x/2, 0 ); chamfered_corner.Move( corner_pos ); outline.BooleanAdd( chamfered_corner, SHAPE_POLY_SET::PM_STRICTLY_SIMPLE ); } // Now chamfer this corner chamfered_corner.RemoveAllContours(); chamfered_corner.NewOutline(); chamfered_corner.Append( 0, 0 ); chamfered_corner.Append( 0, signY[ii]*chamfer_value ); chamfered_corner.Append( signX[ii]*chamfer_value, 0 ); chamfered_corner.Move( corner_pos ); outline.BooleanSubtract( chamfered_corner, SHAPE_POLY_SET::PM_STRICTLY_SIMPLE ); } // Rotate and move the outline: if( aRotation != 0.0 ) outline.Rotate( DECIDEG2RAD( -aRotation ), VECTOR2I( 0, 0 ) ); outline.Move( VECTOR2I( aPosition ) ); // Add the outline: aCornerBuffer.Append( outline ); }