How to draw randomized shapes in a gridLayout of JPanels - java

What the code should do is draw X randomized shapes with the click of a button. What I have so far is a subclass that creates the random shape to be placed inside the JPanels, but the issue is that the same shape is used in all panels, I need each shape to be randomized.
The subclass looks like this:
public class Shapes extends JPanel
{
Random rand = new Random();
private int x = 5;
private int y = 5;
private int diameter = 200;
private Color outline;
private Color internal;
private Color internal2;
private Color internal3;
public Shapes() {
this(new Random());
}
//randomizes colors
public Shapes(Random rand) {
outline = new Color(rand.nextInt(256), rand.nextInt(256), rand.nextInt(256));
internal = new Color(rand.nextInt(256), rand.nextInt(256), rand.nextInt(256));
internal2 = new Color(rand.nextInt(256), rand.nextInt(256), rand.nextInt(256));
internal3 = new Color(rand.nextInt(256), rand.nextInt(256), rand.nextInt(256));
}
{
super.paintComponent(g);
g.setColor(outline);
g.drawOval(x, y, diameter, diameter);
g.setColor(internal);
g.fillOval(x+2, y+2, diameter-4, diameter-4);
g.setColor(internal2);
g.fillOval(x+25, y+66, diameter/3, diameter/3);
g.fillOval(x+125, y+66, diameter/3, diameter/3);
g.setColor(internal3);
g.fillArc(x+55, y+105, diameter/3, diameter/3, 180, 180);
}
}
While the main class looks like so [currently set up to make 6 images]:
public class ShapeGrid extends JFrame implements ActionListener
{
private JButton button;
int i = 2;
int j = 3;
JPanel[][] panelHolder = new JPanel[i][j];
private Shapes shapes;
public static void main(String[] args)
{
ShapeGrid myGrid = new ShapeGrid();
myGrid.setSize(800, 800);
myGrid.createGUI();
myGrid.setVisible(true);
}
public ShapeGrid()
{
setLayout(new GridLayout(i,j, 5, 5));
for(int m = 0; m < i; m++) {
for(int n = 0; n < j; n++) {
panelHolder[m][n] = new JPanel();
add(panelHolder[m][n]);
}
}
}
private void createGUI()
{
shape = new Shapes();
setDefaultCloseOperation(EXIT_ON_CLOSE);
button = new JButton("Press me");
add(button);
button.addActionListener(this);
}
public void actionPerformed(ActionEvent ae)
{
if (ae.getSource() == button) {
for(int m = 0; m < i; m++) {
for(int n = 0; n < j; n++) {
shape.paintComponent(panelHolder[m][n].getGraphics());
}
}
}
}
}

shape.paintComponent(panelHolder[m][n].getGraphics());
You should NEVER invoke paintCopmonent() directly and you should never use getGraphics(). Swing will determine when a component needs to be painted and Swing will pass the Graphics object to the paintComponent() method.
Once you follow the advice below there is no need for the "Press Me" button (or the above) because the Shapes will be created when the when the ShapeGrid class is created. And the Shapes will automatically be painted when the frame is made visible.
is that the same shape is used in all panels,
You only ever create a single Shape object. You need to create a Shape object for each grid. So in the constructor of the ShapeGrid you need to create one Shape object for each grid.
I suggest you should pass in the rows/columns you want for your grid (instead of hardcoding i/j in your class). So your ShapeGrid constructor code might be something like:
public ShapeGrid(int rows, columns)
{
setLayout(new GridLayout(rows, columns, 5, 5));
int drawShapes = rows * columns;
for(int i = 0; i < drawShapes; i++) {
add( new Shape() );
}
}
That's it. There is no need for the panel holder. You will now have uniques Smileys added to the frame.
Then in your main() method you do something like:
ShapeGrid myGrid = new ShapeGrid(2, 3);

Related

The method setColor(Color) is undefined for the type JFrame

Error code shown below :
The method setColor(Color) is undefined for the type JFrame
package task3;
class Execute {
public static void main(String[] args) throws Exception {
Controller c = new Controller();
c.simulate();
}
}
class Model {
int l; //line
int c; //column
int[] loc = {0,0}; //location
JFrame frame;
int m_width;
int m_height;
int R = (int)(Math.random()*256);
int G = (int)(Math.random()*256);
int B = (int)(Math.random()*256);
public int getRectWidth() {
return frame.getContentPane().getWidth() / c;
}
public int getRectHeight() {
return frame.getContentPane().getHeight() / l;
}
Model(int width, int height, int line, int column, JFrame w) {
m_width = width;
m_height = height;
l = line;
c = column;
frame = w;
}
}
class View extends JComponent {
private Model m_Mod;
View(Model mod) {
m_Mod = mod;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
for(int i=0; i<m_Mod.l; ++i) {
for(int j=0; j<m_Mod.c; ++j) {
g.fillRect(m_Mod.loc[0], m_Mod.loc[1], m_Mod.getRectWidth(), m_Mod.getRectHeight());
m_Mod.loc[0] = m_Mod.loc[0] + m_Mod.getRectWidth() + 2;
}
m_Mod.loc[0] = 0;
m_Mod.loc[1] = m_Mod.loc[1] + m_Mod.getRectHeight() + 2;
}
m_Mod.loc[1] = 0;
}
}
class Controller extends JFrame{
private Model m_Mod;
private View m_View;
Controller(){
JFrame window = new JFrame();
Color color = new Color(m_Mod.R, m_Mod.G, m_Mod.B);
m_Mod.frame.setColor(color);
// Error code here : The method setColor(Color) is undefined for the type JFrame
m_Mod = new Model(500,500,3,8, window);
m_View = new View(m_Mod);
window.add(m_View);
window.setSize(m_Mod.m_width,m_Mod.m_height);
window.setLocationRelativeTo(null);
window.setVisible(true);
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
void simulate(){
//m_View.repaint();
}
}
If I comment the Color section out the program works perfectly. Any ideas?
Furthermore the setColor() method is in the Java Frame class,no? So why is it not working?
Some sites tell me to update my library
Furthermore the setColor() method is in the Java Frame class,no?
Did you read the API? That is the first step you do when attempting to solve a problem.
You can't set the color of a JFrame. You can set the foreground/background of components added to the frame.
Typically you would use the setBackground() on the panel that you add to the frame:
m_View = new View(m_Mod);
m_View.setBackground(...); // added
window.add(m_View);
However this won't work because you are extending JComponent which doesn't paint the background by default.
If you want to extend JComponent then you need to add painting code:
super.paintComponent( g );
g.setColor( getBackground() );
g.fillRect(0, 0, getWidth(), getHeight());
g.setColor( getForeground() ); // default color for your custom painting
Or, the easier approach is to just extend JPanel since a JPanel will automatically paint the background with the color you set.
The setColor() method needs to be called inside the paintComponent method. That was my mistake here. Now it works and the rectangles are drawed in color.

How how to get the cirlces to respond to the timer?

I am in the middle of doing a project and I need some help with make the circles that I am drawing to respond to my timer. I want to make them go slower and faster using my slider that I put in. Attached is the code below. (I know that my code is going to be a jumbled mess, I am using some source code that our professor provide to get the slider on my screen. I am just having a hard time getting the slider/timer talk to my circles.)
import java.util.ArrayList;
import java.util.Random;
import java.awt.*;
import java.awt.event.*;
import javax.swing. *;
import javax.swing.event.*;
import SpeedControlPanel.SlideListener;
public class DotsPanel extends JPanel
{
private final int SIZE = 6; // radius of each dot
private static final long serialVersionUID = 1L;
private ArrayList<Point> pointList;
private Timer timer;
private JLabel lLabel;
private JSlider sSlider;
private JPanel pPanel;
private Circle bouncingBall;
int sSliderHt;
private int moveX, moveY;
AnimationListener animationList;
//-----------------------------------------------------------------
// Constructor: Sets up this panel to listen for mouse events.
//-----------------------------------------------------------------
public DotsPanel()
{
timer = new Timer(30, new AnimationListener());
this.setLayout(new BorderLayout());
bouncingBall = new Circle(SIZE);
Random rand = new Random();
pointList = new ArrayList<Point>();
addMouseListener (new DotsListener());
addMouseMotionListener( new DotsListener());
setBackground(Color.black);
setPreferredSize(new Dimension(500, 300));
lLabel= new JLabel("Timer Delay");
lLabel.setAlignmentX(Component.LEFT_ALIGNMENT);
sSlider = new JSlider(JSlider.HORIZONTAL, 0, 200, 30);
sSlider.setMajorTickSpacing(40);
sSlider.setMinorTickSpacing(10);
sSlider.setPaintTicks(true);
sSlider.setPaintLabels(true);
sSlider.setAlignmentX(Component.LEFT_ALIGNMENT);
sSlider.addChangeListener(new SlideListener());
pPanel= new JPanel();
pPanel.add(lLabel);
pPanel.add(sSlider);
add(pPanel, BorderLayout.SOUTH);
animationList = new AnimationListener();
animationList.timer.start();
/*int[] xArray = new int[1000];
int[] yArray = new int[1000];
for(int i = 0; i < xArray.length; i++)
{
xArray[i] = rand.nextInt(10) + 1;
yArray[i] = rand.nextInt(10) + 1;
}*/
}
//-----------------------------------------------------------------
// Draws all of the dots stored in the list.
//-----------------------------------------------------------------
public void paintComponent(Graphics page)
{
Random rand = new Random();
int R = rand.nextInt(256);
int G = rand.nextInt(256);
int B = rand.nextInt(256);
super.paintComponent(page);
page.setColor(new Color(R%255, (G*3)%255, (B+128)%255));
for (Point spot : pointList)
//page.fillOval(spot.x-SIZE, spot.y-SIZE, SIZE*2, SIZE*2);
page.fillOval(spot.x, spot.y, SIZE*2, SIZE*2);
page.drawString("Count: " + pointList.size(), 5, 15);
}
private class AnimationListener implements ActionListener {
int xRand[];
int yRand[];
Random rand;
public AnimationListener() {
rand = new Random();
xRand = new int[1000];
yRand = new int[1000];
//Filling random values between 0 to 10
for (int i = 0; i < 1000; i++) {
xRand[i] = rand.nextInt(10) + 1;
yRand[i] = rand.nextInt(10) + 1;
}
}
private Timer timer = new Timer(50, this);
#Override
public void actionPerformed(ActionEvent e) {
//Put here for the bounce off of the wall
Rectangle window = getBounds();
for (int i = 0; i < pointList.size(); i++) {
Point spot = pointList.get(i);
spot.x += xRand[i];
spot.y += yRand[i];
if (spot.x <= 0) {
xRand[i] = Math.abs(xRand[i]);
} else if (spot.x >= window.width - (SIZE*2)) {
xRand[i] = -Math.abs(xRand[i]);
}
if (spot.y <= 0) {
yRand[i] = Math.abs(yRand[i]);
} else if (spot.y >= window.height - (SIZE*2)) {
yRand[i] = -Math.abs(yRand[i]);
}
}
bouncingBall.move(moveX, moveY);
// change direction if ball hits a side
int x = bouncingBall.getX();
int y = bouncingBall.getY();
if (x < 0 || x >= WIDTH - SIZE)
{
moveX = moveX * -1;
}
if (y <= 0 || y >= HEIGHT - SIZE)
{
moveY = moveY * -1;
}
sSliderHt =sSlider.getSize().height;
repaint();
repaint();
}
}
//*****************************************************************
// Represents the listener for mouse events.
//*****************************************************************
private class DotsListener implements MouseListener, MouseMotionListener
{
//--------------------------------------------------------------
// Adds the current point to the list of points and redraws
// the panel whenever the mouse button is pressed.
//--------------------------------------------------------------
public void mousePressed(MouseEvent event)
{
pointList.add(event.getPoint());
repaint();
}
public void mouseDragged(MouseEvent event) {
pointList.add(event.getPoint());
repaint();
}
//--------------------------------------------------------------
// Provide empty definitions for unused event methods.
//--------------------------------------------------------------
public void mouseClicked(MouseEvent event) {}
public void mouseReleased(MouseEvent event) {}
public void mouseEntered(MouseEvent event) {}
public void mouseExited(MouseEvent event) {}
}
private class SlideListener implements ChangeListener
{
// ------------------------------------------------
// Called when the state of the slider has changed;
// resets the delay on the timer.
// ------------------------------------------------
public void stateChanged (ChangeEvent event)
{
//int sSliderHt =sSlider.getSize().height;
timer.setDelay(sSlider.getValue());
}
}
}
I want to make them go slower and faster using my slider that I put in.
So it looks to me like you already have logic to make each circle move a random distance each time the Timer fires.
So, just use the slider to change the interval at which the timer fires. Then each time the Timer fires you set the new location of the circles based on your random distance.
Other issues:
The paintComponent(...) method is for painting only. It should NOT set properties of the class. For example you should not be generating random values in the method. You can't control when the paintComponent() is invoked and you don't want to randomly change the properties of the objects you are painting.
Don't use an Array to hold random values. First of all you don't know how many object will be added so you don't want to set a size limit. Instead use an ArrayList.
Create a custom object to contain all the properties you want to control. So you would have the size/location/color etc for each circle you want to paint in a custom object.
See: get width and height of JPanel outside of the class for an example using the above suggestions.

Cannot display image icon

I have to display images using the fly weight pattern, I can't get the images to print to screen, here's the code that demonstrates the problem.
public void draw(Graphics g, int tx, int ty, String name) {
grem.paintIcon(null, g, tx, ty);
g.drawString(name, tx, ty + H + 15 );
ImageIcon grem = new ImageIcon("../images/grem.png");
}
/// next class that calls the above class
public void paint(Graphics g) {
Folder folderIcon;
String name;
int j = 0; //count number in row
int row = Top; //start in upper left
int x = Left;
//go through all the names and folders
for (int i = 0; i< names.size(); i++) {
name = (String)names.elementAt(i);
if (name.equals(selectedName))
folderIcon = fact.getFolder(true);
else
folderIcon = fact.getFolder(false);
//have that folder draw itself at this spot
folderIcon.paint(g);
x = x + HSpace; //change to next posn
j++;
if (j >= HCount) { //reset for next row
j = 0;
row += VSpace;
x = Left;
}
}
}
Don't override paint(). Custom painting is done by overriding paintComponent().
Don't do I/O in a painting method. You can't control when Swing will repaint a component so you don't want to read images in the painting method. The images should be read in the constructor of your class.
Override the getPreferredSize(...) method to return the size of your component, otherwise the size of the component will be (0, 0) so there may be nothing to paint (depending on the layout manager being used.
If you need more help the post a proper SSCCE that demonstrates the problem because we don't know the context of how your code is being used and don't have time to spend guessing what you may or may not be doing.
Read the section from the Swing tutorial on Custom Painting for more information. Also, instead of doing custom painting you could also use a JList to display the Icon in a grid pattern. Check out the table of contents for the tutorial link to find the section on How to Use Lists for more information.
Maybe the null is the problem : it should be something like this if im not mistaken
class MyComponent extends JComponent {
public void paint(Graphics g) {
ImageIcon icon = new ImageIcon("a.png");
int x = 0;
int y = 100;
icon.paintIcon(this, g, x, y);
}
public class Gremlin extends JFrame implements ActionListener {
String names[] = {"Andy","Bill","Bob","Dan","Eugene","Frank","Gary","Harry","Ian","Jack",
"Killlian","Liam","Mark","Nial","Obi","Phil","Richard","Stephan","Terry","Viny",}; // 20 names
public Icon img = new ImageIcon("grem1.jpg");
public JLabel grem = new JLabel(img);
JLabel bigLabel = new JLabel();
JLabel grem2 = new JLabel("New Gremlin");
public JPanel panel2 = new JPanel();
JPanel panel = new JPanel();
public Gremlin() {
JButton button = new JButton("Add Gremlin");
this.add(panel);
panel.setLayout(new GridLayout(9,6));
panel.add(panel2);
panel2.add(button);
for(int i = 0; i<20; i++){
bigLabel.add(grem = new JLabel(names[i]), panel.add(grem = new JLabel(img)));
panel.add(bigLabel);
}
button.addActionListener(this);
setSize(550,600);
setLocationRelativeTo(null);
setVisible(true);
}
public static void main(String[] args) {
Gremlin frame = new Gremlin();
}
#Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
if(e.getSource() != null ){
System.out.println("add a Gremlin");
panel.add(grem = new JLabel("NEW GREMLIN"), panel.add(grem = new JLabel(img)));
revalidate();
}
}
}

How do you make a visual grid in Java

So I am new to Swing and JFrames and such so I kind of struggle with this subject. I'm trying to create a Game of Life application and visual showing it. They underlaying "maths" is already done but I have no clue on how to visually show it. What I am looking for is some kind of grid which I can colour in. This is what I'm talking about: http://en.wikipedia.org/wiki/Conway's_Game_of_Life
You can create a grid for your scope using only standard java swing library.
All you need is drawLine method for the grid layout and fillOvall for paint some cells.
This is a full and running example :
import javax.swing.*;
public class MyGrid extends JApplet {
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.setTitle("Grid Panel Example");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JApplet applet = new MyGrid();
applet.init();
frame.getContentPane().add(applet);
frame.pack();
frame.setVisible(true);
}//end main method
public void init() {
JPanel panel = new GridPanel();
getContentPane().add(panel);
}//end init method
class GridPanel extends JPanel {
int n = 30; //Number of cells of my squeare grid
public GridPanel() {
setPreferredSize(new Dimension(480, 480));
setBackground(Color.BLACK);
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2D = (Graphics2D)g;
g2D.setColor(Color.lightGray);
//Set the cell dimension
int p=0;
int c=16;
int len = c*n;
//Draw the grid
for (int i = 0; i <= n; i++) {
g2D.drawLine(0, p, len, p);
g2D.drawLine(p, 0, p, len);
p += c;
}
//You can paint the (i,j) cell with another color in this way
int i=10;
int j=20;
g2D.setColor(Color.GREEN);
int x = i*c;
int y = j*c;
g2D.fillOval(x, y, c, c);
}//end paintComponent
}//end inner class GridPanel
}//end class
Copy it in a file name MyGrig.java
Then compile it javac MyGrid.java
and finally java MyGrid
A possible solution is this. I create a new class ChangeCellsClass for the logic of painting cells, and i call it's method every second in the appropriate event.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.awt.geom.*;
public class MyGrid extends JApplet {
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.setTitle("Grid Panel Example");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JApplet applet = new MyGrid();
applet.init();
frame.getContentPane().add(applet);
frame.pack();
frame.setVisible(true);
}//end main method
public void init() {
JPanel panel = new GridPanel();
getContentPane().add(panel);
}//end init method
class GridPanel extends JPanel implements ActionListener {
int n = 30; //Number of cells of my squeare grid
boolean[][] cells; //Grid data model
public GridPanel() {
setPreferredSize(new Dimension(480, 480));
setBackground(Color.BLACK);
//Initialize data model
cells = new boolean[n][n];
//Every seconds fire event for update the stste
Timer timer = new Timer(1000, this);
timer.start();
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2D = (Graphics2D)g;
g2D.setColor(Color.lightGray);
//Set the cell dimension
int p=0;
int c=16;
int len = c*n;
//Draw the grid
for (int i = 0; i <= n; i++) {
g2D.drawLine(0, p, len, p);
g2D.drawLine(p, 0, p, len);
p += c;
}
//Draw active cells
g2D.setColor(Color.GREEN);
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
if (cells[i][j]) {
int x = i*c;
int y = j*c;
g2D.fillOval(x, y, c, c);
}
}
}
}//end paintComponent
//Action performed Event
public void actionPerformed(ActionEvent e) {
ChangeCellsClass ccc = new ChangeCellsClass();
cells = ccc.setCells(cells);
repaint();
}//end actionPerformed
}//end inner class GridPanel
}//end class
//This is your class for compute active cells. ChangeCellsClass.java
public class ChangeCellsClass {
public ChangeCellsClass() {
//Some initialization code ....
}//end constructor
public boolean[][] setCells(boolean[][] cells) {
int n = 30; //you may obtain this value dinamically from cells matrix
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
cells[i][j] = Math.random() < 0.1;
}
}
return cells;
}//end method
}//end class

Java, Grid of Objects

I need help with drawing the grids to the GUI as well as the program later letting me change the colour of the boxes drawn. I know i will have to use paintComponent(Graphics g), but i have no idea how or where.
So here is a copy of the code i have got so far ( even though i have been told it can be quite daunting just being given code i think it is the best way for people to help and not just do it for me). From the top it sets values, creates the GUI, calls the GUI, fills a 2d array with boxes( i think). Then in the Boxes class setting values the boxes class will need, then the start of how to draw them (didn't know how to work it out), then some seta methods for the x and y coordinates.
what i would like you to do is show how to have the boxes be drawn to the Jpanel, to make a grid and then to show me how to change the colour to different shades of blue, depending on a external value.
import java.awt.*;
import java.awt.Graphics;
import java.util.*;
import javax.swing.*;
public class NewGrid {
Boxes[][] Boxs;
int BoxesX;
int BoxesY;
NewGrid() {
buildtheGUI();
}
JFrame frame = new JFrame();
JPanel panel = new JPanel();
public void buildtheGUI() {
frame.setSize(500, 500);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(panel);
frame.setVisible(true);
}
public static void main(String[] args) {
new NewGrid();
}
public void addboxes() {
Boxs = new Boxes[panel.getWidth() / 10][panel.getHeight() / 10];
for (int i = 0; i < panel.getWidth() / 10; i++) {
for (int j = 0; j < panel.getHeight() / 10; j++) {
Boxs[i][j] = new Boxes();
Boxs[i][j].setx(i * (panel.getWidth() / 10));
Boxs[i][j].sety(j * (panel.getHeight() / 10));
Boxs[i][j].draw(null);
}
}
}
}
public class Boxes extends JPanel {
int x;
int y;
int width = 10;
int hieight = 10;
Color colour = Color.BLACK;
public void draw(Graphics g) {
g.setColor(colour);
g.fillRect(x, y, width, hieight);
}
public void setx(int i ){
x = i;
}
public void sety(int i ){
y = i;
}
}
I can't comment something, to try to make things easier,
I code there box.putClientProperty(unique_identifier, value_for_identifier), you can to multiple this method as you want
from every Swing Listener you can to get this and proper coordinated defined in putClientProperty
.
JComponent comp = event.getComponent();
String strRow = (String) comp.getClientProperty("row");
String strColumn = (String) comp.getClientProperty("column");
simple code
import java.awt.*;
import java.awt.Graphics;
import java.util.*;
import javax.swing.*;
public class NewGrid {
private int row = 10;
private int column = 10;
private JFrame frame = new JFrame();
private JPanel panel = new JPanel();
private NewGrid() {
addboxes();
panel.setLayout(new GridLayout(row, column));
frame.add(panel);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true);
}
private void addboxes() {
for (int i = 0; i < row; i++) {
for (int j = 0; j < column; j++) {
Boxes box = new Boxes();
box.putClientProperty("row", row);
box.putClientProperty("column", column);
panel.add(box);
}
}
}
public static void main(String[] args) {
Runnable doRun = new Runnable() {
#Override
public void run() {
new NewGrid();
}
};
SwingUtilities.invokeLater(doRun);
}
}
class Boxes extends JPanel {
private static final long serialVersionUID = 1L;
#Override
public Dimension getMinimumSize() {
return new Dimension(20, 20);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(20, 20);
}
#Override
public Dimension getMaximumSize() {
return new Dimension(20, 20);
}
#Override
public void paintComponent(Graphics g) {
int margin = 2;
Dimension dim = getSize();
super.paintComponent(g);
g.setColor(Color.red);
g.fillRect(margin, margin, dim.width - margin * 2,
dim.height - margin * 2);
}
}

Categories

Resources