2011-09-20 13:57:40 +00:00
|
|
|
/**
|
|
|
|
* @file trigo.cpp
|
|
|
|
* @brief Trigonometric functions.
|
|
|
|
*/
|
2007-06-05 12:10:51 +00:00
|
|
|
|
|
|
|
#include "fctsys.h"
|
2011-09-20 13:57:40 +00:00
|
|
|
#include "macros.h"
|
2007-06-05 12:10:51 +00:00
|
|
|
#include "trigo.h"
|
|
|
|
|
2011-09-20 13:57:40 +00:00
|
|
|
|
2009-06-13 17:06:07 +00:00
|
|
|
bool TestSegmentHit( wxPoint aRefPoint, wxPoint aStart, wxPoint aEnd, int aDist )
|
|
|
|
{
|
|
|
|
// make coordinates relatives to aStart:
|
|
|
|
aEnd -= aStart;
|
|
|
|
aRefPoint -= aStart;
|
|
|
|
return DistanceTest( aDist, aEnd.x, aEnd.y, aRefPoint.x, aRefPoint.y );
|
|
|
|
}
|
|
|
|
|
2007-06-05 12:10:51 +00:00
|
|
|
|
2009-11-23 15:16:50 +00:00
|
|
|
bool DistanceTest( int seuil, int dx, int dy, int spot_cX, int spot_cY )
|
2007-08-08 20:51:08 +00:00
|
|
|
{
|
2011-09-20 13:57:40 +00:00
|
|
|
/* We can have 4 cases::
|
|
|
|
* horizontal segment
|
|
|
|
* vertical segment
|
|
|
|
* 45 degrees segment
|
|
|
|
* other slopes
|
|
|
|
*/
|
2009-11-23 15:16:50 +00:00
|
|
|
int cXrot, cYrot, segX, segY;
|
|
|
|
int pointX, pointY;
|
2007-08-08 20:51:08 +00:00
|
|
|
|
2009-02-04 15:25:03 +00:00
|
|
|
segX = dx;
|
|
|
|
segY = dy;
|
|
|
|
pointX = spot_cX;
|
|
|
|
pointY = spot_cY;
|
2007-08-08 20:51:08 +00:00
|
|
|
|
2011-09-20 13:57:40 +00:00
|
|
|
/* Recalculating coord for the segment is in 1st quadrant (coord >= 0) */
|
2009-11-23 15:16:50 +00:00
|
|
|
if( segX < 0 ) /* set > 0 by symmetry about the Y axis */
|
2007-08-08 20:51:08 +00:00
|
|
|
{
|
2009-02-04 15:25:03 +00:00
|
|
|
segX = -segX;
|
|
|
|
pointX = -pointX;
|
2007-08-08 20:51:08 +00:00
|
|
|
}
|
2011-09-20 13:57:40 +00:00
|
|
|
|
2009-11-23 15:16:50 +00:00
|
|
|
if( segY < 0 ) /* set > 0 by symmetry about the X axis */
|
2007-08-08 20:51:08 +00:00
|
|
|
{
|
2009-02-04 15:25:03 +00:00
|
|
|
segY = -segY;
|
|
|
|
pointY = -pointY;
|
2007-08-08 20:51:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-11-23 15:16:50 +00:00
|
|
|
if( segY == 0 ) /* horizontal */
|
2007-08-08 20:51:08 +00:00
|
|
|
{
|
|
|
|
if( abs( pointY ) <= seuil )
|
|
|
|
{
|
2009-11-23 15:16:50 +00:00
|
|
|
if( ( pointX >= 0 ) && ( pointX <= segX ) )
|
2007-08-08 20:51:08 +00:00
|
|
|
return 1;
|
2009-11-23 15:16:50 +00:00
|
|
|
|
|
|
|
if( ( pointX < 0 ) && ( pointX >= -seuil ) )
|
2007-08-08 20:51:08 +00:00
|
|
|
{
|
2011-09-20 13:57:40 +00:00
|
|
|
if( ( ( pointX * pointX ) + ( pointY * pointY ) ) <= ( seuil * seuil ) )
|
2007-08-08 20:51:08 +00:00
|
|
|
return true;
|
|
|
|
}
|
2009-11-23 15:16:50 +00:00
|
|
|
if( ( pointX > segX ) && ( pointX <= ( segX + seuil ) ) )
|
2007-08-08 20:51:08 +00:00
|
|
|
{
|
2009-11-23 15:16:50 +00:00
|
|
|
if( ( ( ( pointX - segX ) * ( pointX - segX ) )
|
2010-02-04 17:46:12 +00:00
|
|
|
+ ( pointY * pointY ) ) <= ( seuil * seuil ) )
|
2007-08-08 20:51:08 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2009-11-23 15:16:50 +00:00
|
|
|
else if( segX == 0 ) /* vertical */
|
2007-08-08 20:51:08 +00:00
|
|
|
{
|
|
|
|
if( abs( pointX ) <= seuil )
|
|
|
|
{
|
2009-11-23 15:16:50 +00:00
|
|
|
if( ( pointY >= 0 ) && ( pointY <= segY ) )
|
2007-08-08 20:51:08 +00:00
|
|
|
return true;
|
2011-09-20 13:57:40 +00:00
|
|
|
|
2009-11-23 15:16:50 +00:00
|
|
|
if( ( pointY < 0 ) && ( pointY >= -seuil ) )
|
2007-08-08 20:51:08 +00:00
|
|
|
{
|
2011-09-20 13:57:40 +00:00
|
|
|
if( ( ( pointY * pointY ) + ( pointX * pointX ) ) <= ( seuil * seuil ) )
|
2007-08-08 20:51:08 +00:00
|
|
|
return true;
|
|
|
|
}
|
2011-09-20 13:57:40 +00:00
|
|
|
|
2009-11-23 15:16:50 +00:00
|
|
|
if( ( pointY > segY ) && ( pointY <= ( segY + seuil ) ) )
|
2007-08-08 20:51:08 +00:00
|
|
|
{
|
2009-11-23 15:16:50 +00:00
|
|
|
if( ( ( ( pointY - segY ) * ( pointY - segY ) )
|
2010-02-04 17:46:12 +00:00
|
|
|
+ ( pointX * pointX ) ) <= ( seuil * seuil ) )
|
2007-08-08 20:51:08 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2009-11-23 15:16:50 +00:00
|
|
|
else if( segX == segY ) /* 45 degrees */
|
2007-08-08 20:51:08 +00:00
|
|
|
{
|
2009-11-23 15:16:50 +00:00
|
|
|
/* Rotate axes of 45 degrees. mouse was then
|
|
|
|
* Coord: x1 = x * y * cos45 + sin45
|
|
|
|
* y1 = y * cos45 - sin45 x *
|
|
|
|
* And the segment of track is horizontal.
|
|
|
|
* Coord recalculation of the mouse (sin45 = cos45 = .707 = 7 / 10
|
|
|
|
* Note: sin or cos45 = .707, and when recalculating coord
|
|
|
|
* dx45 and dy45, lect coeff .707 is neglected, dx and dy are
|
|
|
|
* actually 0707 times
|
|
|
|
* Too big. (security hole too small)
|
|
|
|
* Spot_cX, Y * must be by .707 * .707 = 0.5
|
|
|
|
*/
|
2007-08-08 20:51:08 +00:00
|
|
|
|
|
|
|
cXrot = (pointX + pointY) >> 1;
|
|
|
|
cYrot = (pointY - pointX) >> 1;
|
|
|
|
|
2009-11-23 15:16:50 +00:00
|
|
|
/* Recalculating coord of segment extremity, which will be vertical
|
|
|
|
* following the orientation of axes on the screen: dx45 = pointx
|
|
|
|
* (or pointy) and 1.414 is actually greater, and dy45 = 0
|
|
|
|
*/
|
2007-08-08 20:51:08 +00:00
|
|
|
|
2009-11-23 15:16:50 +00:00
|
|
|
// * Threshold should be .707 to reflect the change in coeff dx, dy
|
2009-02-04 15:25:03 +00:00
|
|
|
seuil *= 7;
|
|
|
|
seuil /= 10;
|
|
|
|
|
2009-11-23 15:16:50 +00:00
|
|
|
if( abs( cYrot ) <= seuil ) /* ok on vertical axis */
|
2007-08-08 20:51:08 +00:00
|
|
|
{
|
2009-11-23 15:16:50 +00:00
|
|
|
if( ( cXrot >= 0 ) && ( cXrot <= segX ) )
|
2007-08-08 20:51:08 +00:00
|
|
|
return true;
|
|
|
|
|
2009-11-23 15:16:50 +00:00
|
|
|
/* Check extremes using the radius of a circle. */
|
|
|
|
if( ( cXrot < 0 ) && ( cXrot >= -seuil ) )
|
2007-08-08 20:51:08 +00:00
|
|
|
{
|
2011-09-20 13:57:40 +00:00
|
|
|
if( ( ( cXrot * cXrot ) + ( cYrot * cYrot ) ) <= ( seuil * seuil ) )
|
2007-08-08 20:51:08 +00:00
|
|
|
return true;
|
|
|
|
}
|
2009-11-23 15:16:50 +00:00
|
|
|
if( ( cXrot > segX ) && ( cXrot <= ( segX + seuil ) ) )
|
2007-08-08 20:51:08 +00:00
|
|
|
{
|
2009-11-23 15:16:50 +00:00
|
|
|
if( ( ( ( cXrot - segX ) * ( cXrot - segX ) )
|
2010-02-04 17:46:12 +00:00
|
|
|
+ ( cYrot * cYrot ) ) <= ( seuil * seuil ) )
|
2007-08-08 20:51:08 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2009-11-23 15:16:50 +00:00
|
|
|
else /* any orientation */
|
2007-08-08 20:51:08 +00:00
|
|
|
{
|
2009-11-23 15:16:50 +00:00
|
|
|
/* There is a change of axis (rotation), so that the segment
|
|
|
|
* track is horizontal in the new reference
|
|
|
|
*/
|
2007-08-08 20:51:08 +00:00
|
|
|
int angle;
|
|
|
|
|
2011-09-20 13:57:40 +00:00
|
|
|
angle = wxRound( ( atan2( (double) segY, (double) segX ) * 1800.0 / M_PI ) );
|
2009-02-04 15:25:03 +00:00
|
|
|
cXrot = pointX;
|
|
|
|
cYrot = pointY;
|
2007-08-08 20:51:08 +00:00
|
|
|
|
2010-02-04 17:46:12 +00:00
|
|
|
RotatePoint( &cXrot, &cYrot, angle ); /* Rotate the point to be tested */
|
|
|
|
RotatePoint( &segX, &segY, angle ); /* Rotate the segment */
|
2007-08-08 20:51:08 +00:00
|
|
|
|
2009-11-23 15:16:50 +00:00
|
|
|
/* The track is horizontal, following the amendments to coordinate
|
|
|
|
* axis and, therefore segX = length of segment
|
|
|
|
*/
|
|
|
|
if( abs( cYrot ) <= seuil ) /* vertical axis */
|
2007-08-08 20:51:08 +00:00
|
|
|
{
|
2009-11-23 15:16:50 +00:00
|
|
|
if( ( cXrot >= 0 ) && ( cXrot <= segX ) )
|
2007-08-08 20:51:08 +00:00
|
|
|
return true;
|
|
|
|
|
2009-11-23 15:16:50 +00:00
|
|
|
if( ( cXrot < 0 ) && ( cXrot >= -seuil ) )
|
2007-08-08 20:51:08 +00:00
|
|
|
{
|
2011-09-20 13:57:40 +00:00
|
|
|
if( ( ( cXrot * cXrot ) + ( cYrot * cYrot ) ) <= ( seuil * seuil ) )
|
2007-08-08 20:51:08 +00:00
|
|
|
return true;
|
|
|
|
}
|
2011-09-20 13:57:40 +00:00
|
|
|
|
2009-11-23 15:16:50 +00:00
|
|
|
if( ( cXrot > segX ) && ( cXrot <= ( segX + seuil ) ) )
|
2007-08-08 20:51:08 +00:00
|
|
|
{
|
2009-11-23 15:16:50 +00:00
|
|
|
if( ( ( ( cXrot - segX ) * ( cXrot - segX ) )
|
2011-09-20 13:57:40 +00:00
|
|
|
+ ( cYrot * cYrot ) ) <= ( seuil * seuil ) )
|
2007-08-08 20:51:08 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2009-11-23 15:16:50 +00:00
|
|
|
|
2007-08-08 20:51:08 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-11-23 15:16:50 +00:00
|
|
|
int ArcTangente( int dy, int dx )
|
2007-06-05 12:10:51 +00:00
|
|
|
{
|
2007-08-04 20:05:54 +00:00
|
|
|
double fangle;
|
|
|
|
|
|
|
|
if( dy == 0 )
|
|
|
|
{
|
|
|
|
if( dx >= 0 )
|
|
|
|
return 0;
|
|
|
|
else
|
|
|
|
return -1800;
|
|
|
|
}
|
|
|
|
|
|
|
|
if( dx == 0 )
|
|
|
|
{
|
|
|
|
if( dy >= 0 )
|
|
|
|
return 900;
|
|
|
|
else
|
|
|
|
return -900;
|
|
|
|
}
|
|
|
|
|
|
|
|
if( dx == dy )
|
|
|
|
{
|
|
|
|
if( dx >= 0 )
|
|
|
|
return 450;
|
|
|
|
else
|
|
|
|
return -1800 + 450;
|
|
|
|
}
|
|
|
|
|
|
|
|
if( dx == -dy )
|
|
|
|
{
|
|
|
|
if( dx >= 0 )
|
|
|
|
return -450;
|
|
|
|
else
|
|
|
|
return 1800 - 450;
|
|
|
|
}
|
|
|
|
|
|
|
|
fangle = atan2( (double) dy, (double) dx ) / M_PI * 1800;
|
2009-04-05 20:49:15 +00:00
|
|
|
return wxRound( fangle );
|
2007-06-05 12:10:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-11-23 15:16:50 +00:00
|
|
|
void RotatePoint( int* pX, int* pY, int angle )
|
2007-06-05 12:10:51 +00:00
|
|
|
{
|
2011-09-20 13:57:40 +00:00
|
|
|
int tmp;
|
2007-08-04 20:05:54 +00:00
|
|
|
|
|
|
|
while( angle < 0 )
|
|
|
|
angle += 3600;
|
|
|
|
|
|
|
|
while( angle >= 3600 )
|
|
|
|
angle -= 3600;
|
|
|
|
|
2011-09-20 13:57:40 +00:00
|
|
|
// Cheap and dirty optimizations for 0, 90, 180, and 270 degrees.
|
2007-08-04 20:05:54 +00:00
|
|
|
if( angle == 0 )
|
|
|
|
return;
|
|
|
|
|
|
|
|
if( angle == 900 ) /* sin = 1, cos = 0 */
|
|
|
|
{
|
|
|
|
tmp = *pX;
|
|
|
|
*pX = *pY;
|
|
|
|
*pY = -tmp;
|
|
|
|
}
|
|
|
|
else if( angle == 1800 ) /* sin = 0, cos = -1 */
|
|
|
|
{
|
|
|
|
*pX = -*pX;
|
|
|
|
*pY = -*pY;
|
|
|
|
}
|
|
|
|
else if( angle == 2700 ) /* sin = -1, cos = 0 */
|
|
|
|
{
|
|
|
|
tmp = *pX;
|
|
|
|
*pX = -*pY;
|
|
|
|
*pY = tmp;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2011-09-20 13:57:40 +00:00
|
|
|
double fangle = DEG2RAD( (double) angle / 10.0 );
|
|
|
|
double fpx = (*pY * sin( fangle ) ) + (*pX * cos( fangle ) );
|
|
|
|
double fpy = (*pY * cos( fangle ) ) - (*pX * sin( fangle ) );
|
2009-04-05 20:49:15 +00:00
|
|
|
*pX = wxRound( fpx );
|
|
|
|
*pY = wxRound( fpy );
|
2007-08-04 20:05:54 +00:00
|
|
|
}
|
2007-06-05 12:10:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-11-23 15:16:50 +00:00
|
|
|
void RotatePoint( int* pX, int* pY, int cx, int cy, int angle )
|
2007-06-05 12:10:51 +00:00
|
|
|
{
|
2007-08-04 20:05:54 +00:00
|
|
|
int ox, oy;
|
2007-06-05 12:10:51 +00:00
|
|
|
|
2008-10-29 15:26:53 +00:00
|
|
|
ox = *pX - cx;
|
2007-08-04 20:05:54 +00:00
|
|
|
oy = *pY - cy;
|
2008-10-29 15:26:53 +00:00
|
|
|
|
2007-08-04 20:05:54 +00:00
|
|
|
RotatePoint( &ox, &oy, angle );
|
|
|
|
|
|
|
|
*pX = ox + cx;
|
|
|
|
*pY = oy + cy;
|
2007-06-05 12:10:51 +00:00
|
|
|
}
|
|
|
|
|
2007-08-04 20:05:54 +00:00
|
|
|
|
2009-11-23 15:16:50 +00:00
|
|
|
void RotatePoint( wxPoint* point, int angle )
|
2007-08-10 18:05:42 +00:00
|
|
|
{
|
|
|
|
int ox, oy;
|
|
|
|
|
2008-10-29 15:26:53 +00:00
|
|
|
ox = point->x;
|
2007-08-10 18:05:42 +00:00
|
|
|
oy = point->y;
|
2008-10-29 15:26:53 +00:00
|
|
|
|
2007-08-10 18:05:42 +00:00
|
|
|
RotatePoint( &ox, &oy, angle );
|
|
|
|
point->x = ox;
|
|
|
|
point->y = oy;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-11-23 15:16:50 +00:00
|
|
|
void RotatePoint( wxPoint* point, const wxPoint& centre, int angle )
|
2007-06-05 12:10:51 +00:00
|
|
|
{
|
2007-08-04 20:05:54 +00:00
|
|
|
int ox, oy;
|
|
|
|
|
2008-10-29 15:26:53 +00:00
|
|
|
ox = point->x - centre.x;
|
2007-08-04 20:05:54 +00:00
|
|
|
oy = point->y - centre.y;
|
2008-10-29 15:26:53 +00:00
|
|
|
|
2007-08-04 20:05:54 +00:00
|
|
|
RotatePoint( &ox, &oy, angle );
|
|
|
|
point->x = ox + centre.x;
|
|
|
|
point->y = oy + centre.y;
|
2007-06-05 12:10:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-08-04 20:05:54 +00:00
|
|
|
void RotatePoint( double* pX, double* pY, double cx, double cy, int angle )
|
2007-06-05 12:10:51 +00:00
|
|
|
{
|
2007-08-04 20:05:54 +00:00
|
|
|
double ox, oy;
|
|
|
|
|
2008-10-29 15:26:53 +00:00
|
|
|
ox = *pX - cx;
|
2007-08-04 20:05:54 +00:00
|
|
|
oy = *pY - cy;
|
2008-10-29 15:26:53 +00:00
|
|
|
|
2007-08-04 20:05:54 +00:00
|
|
|
RotatePoint( &ox, &oy, angle );
|
2008-10-29 15:26:53 +00:00
|
|
|
|
2007-08-04 20:05:54 +00:00
|
|
|
*pX = ox + cx;
|
|
|
|
*pY = oy + cy;
|
2007-06-05 12:10:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-08-04 20:05:54 +00:00
|
|
|
void RotatePoint( double* pX, double* pY, int angle )
|
2007-06-05 12:10:51 +00:00
|
|
|
{
|
2007-08-04 20:05:54 +00:00
|
|
|
double tmp;
|
|
|
|
|
|
|
|
while( angle < 0 )
|
|
|
|
angle += 3600;
|
|
|
|
|
|
|
|
while( angle >= 3600 )
|
|
|
|
angle -= 3600;
|
|
|
|
|
2011-09-20 13:57:40 +00:00
|
|
|
// Cheap and dirty optimizations for 0, 90, 180, and 270 degrees.
|
2007-08-04 20:05:54 +00:00
|
|
|
if( angle == 0 )
|
|
|
|
return;
|
|
|
|
|
|
|
|
if( angle == 900 ) /* sin = 1, cos = 0 */
|
|
|
|
{
|
|
|
|
tmp = *pX;
|
|
|
|
*pX = *pY;
|
|
|
|
*pY = -tmp;
|
|
|
|
}
|
|
|
|
else if( angle == 1800 ) /* sin = 0, cos = -1 */
|
|
|
|
{
|
|
|
|
*pX = -*pX;
|
|
|
|
*pY = -*pY;
|
|
|
|
}
|
|
|
|
else if( angle == 2700 ) /* sin = -1, cos = 0 */
|
|
|
|
{
|
|
|
|
tmp = *pX;
|
|
|
|
*pX = -*pY;
|
|
|
|
*pY = tmp;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2011-09-20 13:57:40 +00:00
|
|
|
double fangle = DEG2RAD( (double) angle / 10.0 );
|
2008-10-29 15:26:53 +00:00
|
|
|
|
2011-09-21 12:51:46 +00:00
|
|
|
double fpx = (*pY * sin( fangle ) ) + (*pX * cos( fangle ) );
|
|
|
|
double fpy = (*pY * cos( fangle ) ) - (*pX * sin( fangle ) );
|
|
|
|
*pX = fpx;
|
|
|
|
*pY = fpy;
|
2007-08-04 20:05:54 +00:00
|
|
|
}
|
2007-06-05 12:10:51 +00:00
|
|
|
}
|
2007-08-08 20:51:08 +00:00
|
|
|
|
|
|
|
|
2010-02-04 17:46:12 +00:00
|
|
|
double EuclideanNorm( wxPoint vector )
|
|
|
|
{
|
|
|
|
return sqrt( (double) vector.x * (double) vector.x + (double) vector.y * (double) vector.y );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
wxPoint TwoPointVector( wxPoint startPoint, wxPoint endPoint )
|
|
|
|
{
|
|
|
|
return endPoint - startPoint;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
double DistanceLinePoint( wxPoint linePointA, wxPoint linePointB, wxPoint referencePoint )
|
|
|
|
{
|
2010-02-10 16:25:13 +00:00
|
|
|
return fabs( (double) ( (linePointB.x - linePointA.x) * (linePointA.y - referencePoint.y) -
|
|
|
|
(linePointA.x - referencePoint.x ) * (linePointB.y - linePointA.y) )
|
|
|
|
/ EuclideanNorm( TwoPointVector( linePointA, linePointB ) ) );
|
2010-02-04 17:46:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool HitTestPoints( wxPoint pointA, wxPoint pointB, double threshold )
|
|
|
|
{
|
|
|
|
wxPoint vectorAB = TwoPointVector( pointA, pointB );
|
|
|
|
double distance = EuclideanNorm( vectorAB );
|
|
|
|
|
|
|
|
return distance < threshold;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int CrossProduct( wxPoint vectorA, wxPoint vectorB )
|
|
|
|
{
|
|
|
|
return vectorA.x * vectorB.y - vectorA.y * vectorB.x;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-03-25 19:16:05 +00:00
|
|
|
double GetLineLength( const wxPoint& aPointA, const wxPoint& aPointB )
|
|
|
|
{
|
|
|
|
return sqrt( pow( (double) aPointA.x - (double) aPointB.x, 2 ) +
|
|
|
|
pow( (double) aPointA.y - (double) aPointB.y, 2 ) );
|
|
|
|
}
|