Checking if a point is inside a rotated rectangle - c#

I know this question has been asked a few times before, and I have read various posts about this. However I am struggling to get this to work.
bool isClicked()
{
Vector2 origLoc = Location;
Matrix rotationMatrix = Matrix.CreateRotationZ(-Rotation);
Location = new Vector2(0 -(Texture.Width/2), 0 - (Texture.Height/2));
Vector2 rotatedPoint = new Vector2(Game1.mouseState.X, Game1.mouseState.Y);
rotatedPoint = Vector2.Transform(rotatedPoint, rotationMatrix);
if (Game1.mouseState.LeftButton == ButtonState.Pressed &&
rotatedPoint.X > Location.X &&
rotatedPoint.X < Location.X + Texture.Width &&
rotatedPoint.Y > Location.Y &&
rotatedPoint.Y < Location.Y + Texture.Height)
{
Location = origLoc;
return true;
}
Location = origLoc;
return false;
}

Let point P(x,y), and rectangle A(x1,y1), B(x2,y2), C(x3,y3), D(x4,y4).
Calculate the sum of areas of △APD, △DPC, △CPB, △PBA.
If this sum is greater than the area of the rectangle:
Then point P(x,y) is outside the rectangle.
Else it is in or on the rectangle.
The area of each triangle can be calculated using only coordinates with this formula:
Assuming the three points are: A(x,y), B(x,y), C(x,y)...
Area = abs( (Bx * Ay - Ax * By) + (Cx * By - Bx * Cy) + (Ax * Cy - Cx * Ay) ) / 2

I assume that Location is the rectangle's rotation center. If not, please update your answer with an appropriate figure.
What you want to do is expressing the mouse location in the local system of the rectangle. Therfore, you could do the following:
bool isClicked()
{
Matrix rotationMatrix = Matrix.CreateRotationZ(-Rotation);
//difference vector from rotation center to mouse
var localMouse = new Vector2(Game1.mouseState.X, Game1.mouseState.Y) - Location;
//now rotate the mouse
localMouse = Vector2.Transform(localMouse, rotationMatrix);
if (Game1.mouseState.LeftButton == ButtonState.Pressed &&
rotatedPoint.X > -Texture.Width / 2 &&
rotatedPoint.X < Texture.Width / 2 &&
rotatedPoint.Y > -Texture.Height / 2 &&
rotatedPoint.Y < Texture.Height / 2)
{
return true;
}
return false;
}
Additionally, you may want to move the check if the mouse is pressed to the beginning of the method. If it is not pressed, you don't have to calculate the transformation etc.

Related

OpenTK / OpenGL - Rotating camera with mouse

Background
I currently have an object that I am displaying that will always be at the origin. I have a function which increments my x and y angles and then calculates the new x,y,z coordinate for the camera:
Public Sub update_rotation()
If cam.manual_lookat = True Then
If camangley >= 360 Then
camangley = camangley - 360
End If
If camanglex >= 360 Then
camanglex = camanglex - 360
End If
If camangley < 0 Then
camangley = 360 + camangley
End If
If camanglex < 0 Then
camanglex = 360 + camanglex
End If
If camangley > 90 And camangley <= 270 Then
cam.invert_y = True
Else
cam.invert_y = False
End If
camx = distance * -Sin(camanglex * (PI / 180)) * Cos((camangley) * (PI / 180))
camy = distance * -Sin((camangley) * (PI / 180))
camz = distance * Cos((camanglex) * (PI / 180)) * Cos((camangley) * (PI / 180))
cam.Position.X = camx
cam.Position.Y = camy
cam.Position.Z = camz
cam.lookat.X = 0
cam.lookat.Y = 0
cam.lookat.Z = 0
' Label2.Text = camanglex & "," & camangley
End If
End Sub
I have this set up to use keyboard events.. the X button adds to the camanglex variable, the Y button adds to the camangley variable, and the Z button adds to the distance variable.
Everything works well doing it this way, using the keyboard.
Problem
I am trying to now use the mouse to handle the rotation instead of the keyboard. I believe this is simply a math question but how do I go about calculating either the new camanglex and camangley variables or directly calculating the new camx,camy,camz ones to establish my cameras new location?
I have a mouse function which will capture the mouse coordinates but am having trouble with the calculation part.
If you want to orbit around object you could trace the path your mouse goes on fake sphere (which is called trackball sometimes). There is a working example of orbit controls.
And here is a pseudocode sketch for something similiar:
mouseDownEventFunction(event) {
computeDragPoint (event.mouseX, event.mouseY, &oldDragPoint);
isMouseDown = true;
}
mouseUpEventFunction(event) {
isMouseDown = false;
}
mouseMoveEventFunction(event) {
if (isMouseDown) {
computeDragPoint (event.mouseX, event.mouseY, &newDragPoint);
rotateCamera (oldDragPoint, newDragPoint);
oldDragPoint = newDragPoint;
}
}
Here we find a point on trackball:
/* we want to ray trace a point on face side of fake sphere.
dragPoint* is our result*/
computeDragPoint(int x, int y, Vector3* dragPoint) {
/* normalize x and y to [-1, 1] so they match flat circle position on screen.
And assign this to dragPoint->x and dragPoint->y.
This part depends on what you want to achieve and input units of x, y.
Keep in mind aspect ratio */
dragPoint->x = (2*x/screenWidth - 0.5) * (screenHeight/screenWidth);
dragPoint->y = 2*y/screenHeight - 0.5;
dragPoint->x /= sqrt(dragPoint->x*dragPoint->x + dragPoint->y*dragPoint->y);
dragPoint->y /= sqrt(dragPoint->x*dragPoint->x + dragPoint->y*dragPoint->y);
/* Then having two values in [-1,1] compute corresponding z */
float tmp = dragPoint->x*dragPoint->x + dragPoint->y*dragPoint->y;
if (tmp > 1) {
dragPoint.x /= sqrt(tmp);
dragPoint.y /= sqrt(tmp);
}
dragPoint->z = -sqrt (1 - dragPoint->x^2 - dragPoint->y^2) || 0;
}
And rotation math can be done with quaternions (or just matrices from Euler angles if you don't want to use quats):
rotateCamera(oldDragPoint, newDragPoint) {
Quaternion quat = new Quaternion (Config.AngleX, Config.AngleY, Config.AngleZ);
quat.normalize();
Quaternion deltaQuat = new Quaternion();
deltaQuat = deltaQuat.setFromUnitVectors(oldDragPoint, newDragPoint);
quat = quat.multiply(deltaQuat);
quat.normalize();
Vector3 resultAngles = Vector3();
resultAngles.setFromQuaternion(quat);
setCameraPositionFromAngles(resultAngles);
}
And in setCameraPositionFromAngles(resultAngles) set your final X, Y, Z coordinates by applying three basic rotations to your initial camera position. So your main parameters are resultAngles. You renew angles and then set position.
Maybe this working example will explain better.

GDI+ curve "overflowing"

I'm currently using GDI+ to draw a line graph, and using Graphics.DrawCurve to smooth out the line. The problem is that the curve doesn't always match the points I feed it, and that makes the curve grow out of the graph frame in some points, as seen below(red is Graphics.DrawLines, green is Graphics.DrawCurve).
How would I go about solving this?
The simplest solution is to set a tension:
The green curve is drawn with the default tension, the blue one set a tension of 0.1f:
private void panel1_Paint(object sender, PaintEventArgs e)
{
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
e.Graphics.DrawLines(Pens.Red, points.ToArray());
e.Graphics.DrawCurve(Pens.Green, points.ToArray());
e.Graphics.DrawCurve(Pens.Blue, points.ToArray(), 0.1f);
}
You will need to test what is the best compromise, 0.2f is still ok, 0.3f is already overdrawing quite a bit..
For a really good solution you will need to use DrawBeziers. This will let you draw curves that can go through the points without any overdrawing and with full control of the radius of the curves; but to to so you will need to 'find', i.e. calculate good control points, which is anything but trivial..:
This result is by no means perfect but already complicated enough.. I have displayed the curve points and their respective control points in the same color. For each point there is an incoming and an outgoing control point. For a smooth curve they need to have the same tangents/gradients in their curve points.
I use a few helper functions to calculate a few things about the segments:
A list of gradients
A list of signs of the gradients
A list of segment lengths
Lists of horizontal and of vertical gaps between points
The main function calculates the array of bezier points, that is the curve points and between each pair the previous left and the next right control points.
In the Paint event it is used like this:
List<PointF> bezz = getBezz(points);
using (Pen pen = new Pen(Color.Black, 2f))
e.Graphics.DrawBeziers(pen, bezz.ToArray());
Here are the functions I used:
List<float> getGradients(List<PointF> p)
{
List<float> grads = new List<float>();
for (int i = 0; i < p.Count - 1; i++)
{
float dx = p[i + 1].X - p[i].X;
float dy = p[i + 1].Y - p[i].Y;
if (dx == 0) grads.Add(dy == 0 ? 0 : dy > 0 ?
float.PositiveInfinity : float.NegativeInfinity);
else grads.Add(dy / dx);
}
return grads;
}
List<float> getLengths(List<PointF> p)
{
List<float> lengs = new List<float>();
for (int i = 0; i < p.Count - 1; i++)
{
float dx = p[i + 1].X - p[i].X;
float dy = p[i + 1].Y - p[i].Y;
lengs.Add((float)Math.Sqrt(dy * dy + dx * dx));
}
return lengs;
}
List<float> getGaps(List<PointF> p, bool horizontal)
{
List<float> gaps = new List<float>();
for (int i = 0; i < p.Count - 1; i++)
{
float dx = p[i + 1].X - p[i].X;
float dy = p[i + 1].Y - p[i].Y;
gaps.Add(horizontal ? dx : dy);
}
return gaps;
}
List<int> getSigns(List<float> g)
{
return g.Select(x => x > 0 ? 1 : x == 0 ? 0 : -1).ToList();
}
And finally the main function; here I make a distinction: Extreme points ( minima & maxima) should have their control points on the same height as the points themselves. This will prevent vertical overflowing. They are easy to find: The signs of their gradients will always altenate.
Other points need to have the same gradient for incoming and outcoming control points. I use the average between the segments' gradients. (Maybe a weighed average would be better..) And I weigh their distance according to the segment lengths..
List<PointF> getBezz(List<PointF> points)
{
List<PointF> bezz = new List<PointF>();
int pMax = points.Count;
List<float> hGaps = getGaps(points, true);
List<float> vGaps = getGaps(points, false);
List<float> grads = getGradients(points);
List<float> lengs = getLengths(points);
List<int> signs = getSigns(grads);
PointF[] bezzA = new PointF[pMax * 3 - 2];
// curve points
for (int i = 0; i < pMax; i++) bezzA[i * 3] = points[i];
// left control points
for (int i = 1; i < pMax; i++)
{
float x = points[i].X - hGaps[i - 1] / 2f;
float y = points[i].Y;
if (i < pMax - 1 && signs[i - 1] == signs[i])
{
float m = (grads[i-1] + grads[i]) / 2f;
y = points[i].Y - hGaps[i-1] / 2f * m * vGaps[i-1] / lengs[i-1];
}
bezzA[i * 3 - 1] = new PointF(x, y);
}
// right control points
for (int i = 0; i < pMax - 1; i++)
{
float x = points[i].X + hGaps[i] / 2f;
float y = points[i].Y;
if (i > 0 && signs[i-1] == signs[i])
{
float m = (grads[i-1] + grads[i]) / 2f;
y = points[i].Y + hGaps[i] / 2f * m * vGaps[i] / lengs[i];
}
bezzA[i * 3 + 1] = new PointF(x, y);
}
return bezzA.ToList();
}
Note that I didn't code for the case of points with the same x-coordinate. So this is ok for 'functional graphs' but not for, say figures, like e.g. stars..
Maybe you just want to look at the "overshooting the bounds" problem as not a problem with the overshoot, but with the bounds. In which case, you can determine the actual bounds of a curve using the System.Drawing.Drawing2D.GraphicsPath object:
GraphicsPath gp = new GraphicsPath();
gp.AddCurve(listOfPoints);
RectangleF bounds = gp.GetBounds();
You can draw that GraphicsPath directly:
graphics.DrawPath(Pens.Black, gp);
As far as solving the bounds problem, the line necessarily overshoots the vertex on some axis. It's easier to see this fact when the lines are aligned to the bounds.
Given these points:
In order for them to be curved, they must exceed their bounds in some way:
If you never want to exceed their vertical bounds, you could simply ensure that the bezier handles have the same Y value as the vertex, but they will overshoot on the X:
Or vice-versa:
You could deliberately undershoot just enough to avoid the way curves can overshoot. This can be done by swapping the bezier handles, which would maybe be at the line-centers, with the vertices:

Tooltip in PictureBox FillPie Coordinates C#

I'm drawing a Circle made of 360 FillPie. Each FillPie color is taken from a List. I want to return a string that says at which degree is the mouse and how much is the value of the list to put it on a tooltip.
List<int> datiDisco = new List<int>();
public void Paint (Graphics grafica)
{
try
{
for (int i = 0; i < datiDisco.Count; i++)
{
Brush penna = new SolidBrush(Color.FromArgb(255, ScalaGrigi(valori[i]), ScalaGrigi(valori[i]), ScalaGrigi(valori[i])));
grafica.FillPie(penna, 0, 0, 400, 400, i, 1.0f);
}
}
catch
{
}
}
until here the code is working and i managed to draw the circle with the correct color.Now i can't figure out how i can take the coordinate of each fillpie that i have drawn. Can someone help me?
Figuring out which pie segment the mouse cursor lies in is a simple application of trigonometry, specifically an application of the inverse tangent (aka arctangent or atan).
As a quick reminder for those who've encountered this before, or as a lesson for those who haven't, let's look quickly at the tangent function. Trigonometry deals with the geometry of right triangles, and by definition a right triangle has two sides and a hypotenuse. The hypotenuse is a special name for the side of the triangle opposite the right (90° or π/2) angle. The other two sides are helpfully just called sides.
The tangent function's value is the ratio of the side opposite an angle to the side adjacent to that angle. The arctangent is the angle whose tangent is equal to that ratio. Because of the symmetry of the function we need to calculate the angle, and then add or subtract an offset depending on the quadrant to extract the 'real' angle. In diagram form this looks like:
The tangent function has discontinuities at several points, namely when the adjacent side's length is 0 (90° and 270°), so we'll have to treat those points specially.
OK, enough math, now on to the practical application.
For this demo, create a new C# WinForms project, and on the default Form1 add a PictureBox.
First, since I don't have your color generation function, I use the following list of values and helper function:
List<int> values = Enumerable.Range(0, 360).ToList();
int Rescale(int x) => (int)(((double)x / 360.0) * 255.0);
In the constructor hook up a couple events, and set some properties:
public Form1()
{
InitializeComponent();
this.pictureBox1.BorderStyle = BorderStyle.Fixed3D;
this.pictureBox1.Size = new Size(50, 50);
this.Size = new Size(450, 450);
this.DoubleBuffered = true;
this.Paint += Form1_Paint;
this.MouseMove += Form1_MouseMove;
}
To paint the circle I use a slightly modified version of your OnPaint handler:
private void Form1_Paint(object sender, PaintEventArgs e)
{
e.Graphics.Clear(Color.Black);
for (int i = 0; i < values.Count; i++)
{
Brush b = new SolidBrush(Color.FromArgb(255, Rescale(values[i]), 0, 0));
e.Graphics.FillPie(b, 0, 0, 400, 400, (float)i, 1.0f);
}
}
In the MouseMove event is where we do most of the heavy lifting:
private void Form1_MouseMove(object sender, MouseEventArgs e)
{
this.pictureBox1.Location = new Point(e.X + 5, e.Y - 5);
int segment = (int)GetAngle(new Rectangle(0, 0, 400, 400), e.Location);
this.pictureBox1.BackColor = Color.FromArgb(255, Rescale(segment), 0, 0);
}
You may notice that since there are 360 wedges are in increments of a degree, I just truncated the angle. If you need more precision, or you decide to use segments greater than 1 degree, then you could use various rounding algorithms to round the angle to the nearest section of the pie.
At last, we're ready to implement the GetAngle function. First we calculate the center of the circle, because everything is relative to that.
int cx = (rect.Width + rect.X) / 2;
int cy = (rect.Height + rect.Y) / 2;
Next calculate the difference between the mouse's position and the center of the rectangle. (I've inverted the y coordinate to line up with 'standard' Cartesian coordinates, to make things easier, and match the coordinates you'd see in a math textbook.)
float x = pTo.X - cx;
float y = (cy - pTo.Y);
Next check for the arctangent's undefined points (and a couple of shortcuts we can take):
if ((int)x == 0)
{
if (y > 0) return 270;
else return 90;
}
else if ((int)y == 0)
{
if (x > 0) return 0;
else return 180;
}
Calculate the internal angle:
float ccwAngle = (float)Math.Atan(Math.Abs(y) / Math.Abs(x));
And map that angle to the appropriate quadrant:
if (x > 0 && y > 0)
{
}
else if (x < 0 && y > 0)
{
ccwAngle = (float)Math.PI - ccwAngle;
}
else if (x < 0 && y < 0)
{
ccwAngle = ccwAngle + (float)Math.PI;
}
else if (x > 0 && y < 0)
{
ccwAngle *= -1f;
}
Convert the angle from degrees to radians and normalize (make sure it's between 0° and 360°)
ccwAngle *= (float)(180 / Math.PI);
while (ccwAngle > 360) ccwAngle -= 360;
while (ccwAngle < 0) ccwAngle += 360;
Finally convert the counter-clockwise angle we needed to do the math into the clockwise angle that GDI uses, and return the value:
return 360f - ccwAngle;
All that together produces the final result:
(The code above is also available as a complete example in this gist)

Alternatives to the HitTest()

Currently my software uses the HitTest() method of a chart object in MSCharts but as I scale this up to more and more data points on my chart combined with other factors this can have a massive performance hit.
I was wondering if there any alternatives that you know of to provide the same functionality ( get the X Coordinate on the chart for the cursor position ) but without the performance hit as hit testing seems to be a very brute force way of obtaining my answer.
My chart is created from the class System.Windows.Forms.DataVisualization.Charting.Chart
Edit for clarity: I need to find the position of a line on my chart to use it for other calculations.
Had the same performance issue with a mousewheel event.
Here is my solution:
To get the axes values of the current mouse position:
double posX = Math.Round(currentArea.AxisX.PixelPositionToValue(e.X));
double posY = Math.Round(currentArea.AxisY.PixelPositionToValue(e.Y));
Taken from Showing Mouse Axis Coordinates on Chart Control with a little change to get it more accurate.
But you should check before, that the mouse is in a ChartArea, else it will throw you an Exception.
To get the ChatElement on which the mouse points:
// Gets the ChartArea that the mouse points
private ChartArea mouseinChartArea(Chart source, Point e)
{
double relativeX = (double)e.X * 100 / source.Width;
double relativeY = (double)e.Y * 100 / source.Height;
foreach (ChartArea ca in source.ChartAreas)
{
if (relativeX > ca.Position.X && relativeX < ca.Position.Right &&
relativeY > ca.Position.Y && relativeY < ca.Position.Bottom)
return ca;
}
return null;
}
// for my purpose, returns an axis. But you can return anything
private Axis findAxisforZooming(Chart source, Point e)
{
ChartArea currentArea = mouseinChartArea(source, new Point(e.X, e.Y)); // Check if inside
if (currentArea == null)
return null;
double axisXfontSize = currentArea.AxisX.LabelAutoFitMinFontSize + ((double)source.Width / SystemInformation.PrimaryMonitorSize.Width)
* (currentArea.AxisX.LabelAutoFitMaxFontSize - currentArea.AxisX.LabelAutoFitMinFontSize);
double axisYfontSize = currentArea.AxisY.LabelAutoFitMinFontSize + ((double)source.Height / SystemInformation.PrimaryMonitorSize.Height)
* (currentArea.AxisY.LabelAutoFitMaxFontSize - currentArea.AxisY.LabelAutoFitMinFontSize);
double axisYfontHeightSize = (axisYfontSize - currentArea.AxisY.LabelStyle.Font.Size) + currentArea.AxisY.LabelStyle.Font.Height;
Graphics g = this.CreateGraphics();
if (currentArea.AxisX.LabelStyle.Font.Unit == GraphicsUnit.Point)
axisXfontSize = axisXfontSize * g.DpiX / 72;
if (currentArea.AxisY.LabelStyle.Font.Unit == GraphicsUnit.Point)
axisYfontHeightSize = axisYfontHeightSize * g.DpiX / 72;
g.Dispose();
// Replacing the SystemInformation.PrimaryMonitorSize with the source.Width / Height will give the accurate TickMarks size.
// But it doens't count for the gab between the tickMarks and the axis lables (so by replacing, it give a good proximity with the gab)
int axisYTickMarks = (int)Math.Round(currentArea.AxisY.MajorTickMark.Size / 100 * SystemInformation.PrimaryMonitorSize.Width); // source.Width;
int axisXTickMarks = (int)Math.Round(currentArea.AxisX.MajorTickMark.Size / 100 * SystemInformation.PrimaryMonitorSize.Height); // source.Height;
int leftInnerPlot = (int)Math.Round(currentArea.Position.X / 100 * source.Width +
currentArea.InnerPlotPosition.X / 100 * currentArea.Position.Width / 100 * source.Width);
int rightInnerPlot = (int)Math.Round(currentArea.Position.X / 100 * this.chart1.Width +
currentArea.InnerPlotPosition.Right / 100 * currentArea.Position.Width / 100 * source.Width);
int topInnerPlot = (int)Math.Round(currentArea.Position.Y / 100 * this.chart1.Height +
currentArea.InnerPlotPosition.Y / 100 * currentArea.Position.Height / 100 * source.Height);
int bottomInnerPlot = (int)Math.Round(currentArea.Position.Y / 100 * source.Height +
currentArea.InnerPlotPosition.Bottom / 100 * currentArea.Position.Height / 100 * source.Height);
// Now you got the boundaries of every important ChartElement.
// Only left to check if the mouse is within your desire ChartElement,
// like the following:
bottomInnerPlot += axisXTickMarks + (int)Math.Round(axisXfontSize); // Include AxisX
if (e.X > leftInnerPlot && e.X < rightInnerPlot &&
e.Y > topInnerPlot && e.Y < bottomInnerPlot) // return AxisX if inside the InnerPlot area or on AxisX
return currentArea.AxisX;
else if (e.X > (leftInnerPlot - axisYTickMarks - (int)Math.Round(axisYfontHeightSize)) && e.X < rightInnerPlot &&
e.Y > topInnerPlot && e.Y < bottomInnerPlot) // return AxisY if on AxisY only
return currentArea.AxisY;
return null;
}
As it can be seen, the code is longer than HitTest(). But the run time is shorter.

Looping through the boundary of a rectangle between two points on the rectangle?

Whew, that was a big one.
Right, so I have two points, on the boundary of the rectangle and also on the two lines, which are cast from the origin. The arrangement of P1/P2 is arbitrary, for the sake of simplicity.
My question is, how can I loop through the green area (The smallest area, basically) of the rectangle?
The implementation: I want to create a field-of-vision effect in a game that I am creating. The origin is the player, who can be anywhere on the current viewport (The rectangle). The lines' directions are offset from the direction that the player is facing. I intend to trace all positions on the green area from the origin, checking for obstructions.
I would prefer a code example answer, any language, but preferably C#
I think, what you want is something like this:
All coordinates C with the following criteria are on the green line that is part of the rectangle R:
(P1.y == P2.y)
?
(
C.x >= P1.x && C.x <= P2.x
)
:
(
(C.x >= P2.x && C.x <= R.right && C.y == P2.y) ||
(C.x >= P1.x && C.x <= R.right && C.y == P1.y) ||
(C.x == R.x && C.y <= P1.y && C.y >= P2.y)
)
This assumes that P1 will be below P2 in case they are not on the same line and that P1 will be before P2 if they are on the same line.
If you want to find out which objects are in the "green area"...
Assuming you know the size of the rectangle you can calculate the vertices of the polygon you're interested in (origin, P1, P2 and the visible rectangle corners) then you can loop through your objects to find which are inside using point in polygon detection.
Randolph Franklyn's for example. Returns 1 for interior points and 0 for exterior points...
int pnpoly(int npol, float *xp, float *yp, float x, float y)
{
int i, j, c = 0;
for (i = 0, j = npol-1; i < npol; j = i++) {
if ((((yp[i] <= y) && (y < yp[j])) ||
((yp[j] <= y) && (y < yp[i]))) &&
(x < (xp[j] - xp[i]) * (y - yp[i]) / (yp[j] - yp[i]) + xp[i]))
c = !c;
}
return c;
}
I'd imagine the simplest way to do what you want(field of view) would be to first draw everything to screen, then draw black everywhere you don't want things to be seen.
So basically drawing darkness onto the no fog-of-war screen.

Categories