banner



How To Draw A Heart In Processing

You are struggling with colorlessness, and itching to use your creativity. You desire to build something, something visually impressive, something artsy. Or peradventure yous want to learn programming and brand something impressive every bit soon every bit possible. If so, and so the Processing language is the fashion to go.

Amongst all the programming languages I accept worked with so far, Processing was without a doubt ane of the most entertaining ones. It is a straightforward language - easy to learn, understand and utilize, still it is very powerful. Information technology is about similar you are painting on an empty canvas with lines of code. In that location are no rigid rules or guidelines whatsoever to limit your creativity, the simply limit is your imagination.

Ultimate Guide to the Processing language Part I: The Fundamentals

In college I was a instruction banana of a program that gathered loftier school students and taught them Processing. Virtually of them didn't have a strong programming background, some hadn't even written a unmarried line of code before. In just five days, they were expected to learn the language and build simple games of their own. The success rate was almost a hundred percent, we rarely faced failure. In this article, this is exactly what we'll be doing. I shrank down the entire program into 2 parts. First part, I volition be talking about the language. I will give a bones overview, a walkthrough for Processing and I will requite some tips & tricks. Then in the adjacent part, we will build a simple game pace past step, each step will exist explained in detail. I will too convert the code of the game into JavaScript using p5js, so that our game can run in a web browser.

What You Should Already know

To understand and hands follow these articles, yous should have a bones knowledge of programming, as I will non be talking most programming fundamentals. I will mostly not be touching whatsoever advanced programming concepts though, so a superficial understanding will do. At that place are some parts where I talk about some low level ideas and concepts such as object-oriented programming (OOP), but they are not crucial. Those are for curious readers who are interested in the construction of the language. If yous don't want to know, you tin can just skip those parts. Other than that, the only thing you should accept is the ambition to learn this crawly language and enthusiasm to create your ain game!

How to Follow

I'm always in favor of learning programming past trying and experimenting. The sooner you dive into your ain game, the faster you'll get comfortable with Processing. So that will be my first proposition, try each and every stride in your own environment. Processing has a simple and like shooting fish in a barrel to use IDE (i.e. a lawmaking editor), it is the only affair you lot'll need to download & install to follow. You tin can download information technology from here.

So let's get started!

What is the Processing Language?

This section includes a brief technical overview of the language, its structure and some notes on the compilation and execution process. The details volition include some advanced knowledge on programming and the Java surround. If yous don't heed about details for at present and tin't wait to learn and code your own game, you can skip to the "Fundamentals of Processing" section.

Processing is a visual programming linguistic communication that allows you to sketch with codes, so to speak. However it is non exactly a programming linguistic communication on its own, information technology is what they call a "Coffee-esque" programming linguistic communication, which means the language is built on top of the Java platform, but is not exactly Java per se. It is based on Coffee and all your code gets preprocessed and converted directly into Coffee lawmaking when y'all hit the run button. Java's PApplet course is the base class for all Processing sketches. To requite an example, let'southward take a couple of basic processing code blocks:

          public void setup() {   // setup codes goes here } public void describe() {   // draw codes goes hither }                  

These code blocks will be converted into something similar this:

          public class ExampleFrame extends Frame {       public ExampleFrame() {          super("Embedded PApplet");           setLayout(new BorderLayout());          PApplet embed = new Embedded();          add together(embed, BorderLayout.Center);           embed.init();      } }  public form Embedded extends PApplet {         public void setup() {       // setup codes goes here     }     public void depict() {       // draw codes goes here     }      }                  

Y'all can see that the processing code block was wrapped with a class that extends from Coffee'south PApplet. Therefore, all the classes you define in your processing code, if any, will be treated as inner classes.

The fact that Processing is Java based gives us a lot of advantages, particularly if you lot are a Java programmer. Non only is the syntax familiar, but it likewise gives yous the ability to do things similar embedding Java code, libraries, JAR files in your sketches, using your Processing applets directly in your Java applications, defining classes and using standard data types such equally int, float, char and so on. You can even write your Pocessing code directly from Eclipse, if you want to spend some time to set it upward. One matter yous tin't exercise is utilise AWT or Swing components in your Processing sketches, because they disharmonize with the looping nature of Processing. But don't worry, we will non exist doing any of that fancy stuff in this article.

Fundamentals of Processing

Processing code consists of two main parts, setup and draw blocks. Setup cake runs once when the code gets executed, and the draw blocks runs continuously. The principal thought behind Processing is, what you write within the draw block volition be executed 60 times per second from top to bottom, until your program terminates. We will build everything by taking advantage of this this very thought. Nosotros will make our objects move, go along our scores, notice collisions, implement gravity, and do pretty much everything else using this characteristic. This refresh loop is the heartbeat of our project. I will be explaining how to use this heartbeat to bring your code to life in later sections. Beginning, let me introduce you to the Processing IDE.

Processing IDE

If you take read until this point and still didn't download the Processing IDE, please get ahead and practice it. Throughout the article, I volition outline some easy tasks for you to effort on your own, yous can only practice if you have the IDE up and running. Here is a brief introduction of the processing IDE. It is very elementary and cocky explanatory, then I will keep it brusque.

As you'd expect, run and stop buttons exercise what they suggest. When you click on the run push, your code will go compiled and executed. By nature, processing programs never get terminated, they run forever and ever until they get disturbed. Y'all tin can stop information technology programmatically, however if yous don't, you lot can apply the stop button.

The button that looks like a butterfly on the correct of the run & stop is the debugger. Using the debugger needs a whole other article dedicated to it. It is out of the scope of this commodity, then yous can ignore it for now. The dropdown next to debugger button is where you add / set mods. Mods provide you some certain functionality, allow y'all to write code for Android, let yous to write code in Python, and so on and and then forth. Mods are also out of the telescopic, so you can keep it in the default Java fashion and ignore it also.

The window on the code editor is where your sketches commonly run. In the prototype information technology is blank, because we haven't fix any belongings similar size or background color, or we didn't draw anything.

There is zip much to talk about the code editor, it is simply where you write your code. There are line numbers(!) Older versions of Processing didn't accept that and y'all can't imagine how happy I was when I starting time saw them.

The blackness box below is the console. We volition utilise it to print out stuff for quick debugging purposes. The errors tab next to the panel is where your errors will appear. This is likewise a new useful feature that came with Processing 3.0. In the older versions, the errors were printed to the console and it was hard to keep track of them.

Setup Block

As stated before, setup blocks get executed once when the programme starts. You tin use it for making configurations and for things that you'd like to run only once, for instance, loading images or sounds. Here is an example setup block. Run this lawmaking in your own environment and see the results for yourself.

          public void setup() {   // Size of our sketch will be 800x600,    // and utilize the P2D rendering engine.   size(800, 600, P2D);      // Nosotros could have used this part instead of size()   // fullScreen(P2D);      // The groundwork color of our sketch will be black   // by default, unless specified otherwise   background(0);      // Nosotros could take used this to set a background image.   // Note that size of our sketch should be the same as the image.   // background(loadImage("test.jpg"));      // Shapes and objects volition be filled with blood-red by default,   // unless specified otherwise.   fill(255,0,0);      // Shaped and objects volition have a white border past default,   // unless specified otherwise.   stroke(255); }                  

The methods related with styling (background, fill, stroke) will be explained at the backdrop & settings sections. For now, what you need to know is how the settings and configurations we gear up here affects our whole sketch. Codes written here are used to set some base rulesets applicable throughout the sketch. What you should likewise sympathize in this section are the methods listed beneath:

size() - As the name suggests, this office is used to configure the size of our sketch. It has to be in the starting time line of the setup code block. It could be used in the following forms:

  • size(width,tiptop);
  • size(width, summit, renderer);

The width and elevation values could be given in pixels. Size function accepts a third parameter, renderer, which is used to gear up which rendering engine our sketch will utilize. Past default, the renderer is set to P2D. The available renderers are P2D (Processing 2nd), P3D (Processing 3D, should be used if your sketches volition include 3D graphics) and PDF (2D graphics are drawn direct into an Acrobat PDF file. More inforation can be institute here). P2D and P3D renderers brand employ of OpenGL compatible graphics hardware.

fullScreen() - As of Processing 3.0, fullScreen role can now be used instead of the size() function. Only like the size() office, it should be in the offset line of the setup block as well. The usage is equally follows:

  • fullScreen();
  • fullScreen(display);
  • fullScreen(renderer);
  • fullScreen(brandish, renderer);

If you use information technology without any parameters, your processing sketch will merely run in fullscreen, and will run on your main display. The 'brandish' parameter is used to assail which brandish your sketch will run. For instance if you connect external monitors to your calculator, you tin prepare the display variable to ii (or 3, 4 etc.) and your sketch will run there. The 'renderer' parameter is as explained at the size() function above.

Settings Block

This is another feature that is introduced with the new release of Processing. It is a code block, just similar setup and draw. It is useful when you lot want to define size() or fullScreen() methods with variable parameters. Information technology is too necessary to define size() and other styling properties such equally smooth() in this code cake if you lot are using any environs other than Processing's own IDE, such as Eclipse. Only you will not be needing it in most cases, definitely not in this article.

Draw Cake

At that place is nothing special to talk virtually the draw block, nevertheless everything is special about information technology. Depict cake is where all the magic happens. It is the heart of your programme, chirapsia 60 times a second. This lawmaking block houses all your code logic. All your shapes, objects etc. will be written in hither.

Most of the code we will talk about in this article is going to be from the draw block, so it is important that you clearly sympathise how this code block works. To give y'all a demonstration, here is something you can try. First note that we can print anything to the console by using the impress() or println() methods. Print methods just print to the console, println nonetheless prints and appends a newline at the end, so each println() will print in dissever rows.

So, take a expect at the following code block. First, try to estimate what it will print in the console. And then, go ahead and attempt it out:

          void setup(){ } void draw(){   int x = 0;   x += ane;   print(x+" "); }                  

If you lot guessed "ane 2 3 4…", I got you! This is i of the confusions in Processing. Remember this block repeatedly gets executed? When yous define a variable hither, it gets defined on each loop over and over once more. On each iteration, x is set to 0, gets incremented by ane and gets printed to the console. Therefore nosotros get the result "1 1 1 1…". This example was somewhat obvious, simply it may be disruptive when things get a piffling complicated.

We don't want ten to get overwritten, so how can we achieve this and get the issue "1 2 3 4…" ? By using global variables. This is zippo fancy, we merely define the variable outside of describe block so it doesn't go re-defined on each iteration. Likewise, the scope of the variable will be reachable throughout the sketch. See the code below:

          int 10 = 0;  void setup(){ } void depict(){   ten += one;   print(10+" "); }                  

You might exist asking yourself, how tin a variable defined outside of our blocks work? And why didn't nosotros utilise the setup() block since it gets executed once at the kickoff? The answer is related with object-oriented programming and scopes, if you are not familiar, you may skip this paragraph. Refer to the part where I explained how Processing code gets converted into Java. Remember how they get wrapped with a Java class? The variables we write exterior of setup() and draw() block also gets wrapped, therefore they are treated as fields of the outer form that wraps our code. Using ten+=1 is the same as using this.10+=1. It too functions the same in our case, no variable called x is defined in the telescopic of draw() and an outer scope is searched, which is the scope of this. And why didn't we ascertain our variable ten in the setup() section? If we did, the scope of which x is divers would exist the scope of the setup function and it wouldn't be accessible from the depict() block.

Drawing Shapes & Texts

At present we know how to configure our sketch using the setup block, and we know what draw block does. So it is fourth dimension to become a little visual and learn near the fun parts of processing: how to draw shapes.

Before we begin, you should understand the coordinate system. In Processing, y'all determine the coordinates of every object you draw on the screen. The coordinate system is in pixels. The origin (ie. starting point) is the top left corner, you lot should give your coordinates relative to that point. Another thing you should know is, each shape has a different reference indicate. For example, rect() has its superlative left corner every bit a reference point. For ellipse(), information technology is the center. These reference points can be changed with methods like rectMode() and ellipseMode(), which I will be explaining in the backdrop & settings section. An case figure is provided to assist you empathize amend.

This commodity is a basic overview of Processing, so nosotros will not be touching any circuitous shapes similar vertexes or 3D shapes. Bones 2D shapes will really be more than than enough for us to create our own game. In the effigy, y'all can see examples of how shapes are drawn. Each shape has its ain syntax to be created, but the bones idea is to give either its coordinates or its sizes or both. Here are some shapes y'all should be familiar with (for all values given below, '10' and 'y' means x and y coordinates in pixels, 'westward' and 'h' means width and height values also in pixels):

betoken() - Uncomplicated bespeak, only needs a single coordinate. Usage:

  • betoken(10, y)
  • point(ten, y, z) - In example you are using 3 dimensions.

line() - For creating a line. You can create a line with just a starting and catastrophe signal. Usage:

  • line(x1, y1, x2, y2)
  • line(x1, y1, z1, x2, y2, z2) - In case you lot are using three dimensions.

triangle() - For creating a triangle. Usage: triangle(x1, y1, x2, y2, x3, y3)

quad() - For creating a quadrilateral. Usage: quad(x1, y1, x2, y2, x3, y3, x4, y4)

rect() - For creating a rectangle. The reference point is elevation left corner by default (refer to the figure). Here is the usage:

  • rect(x, y, west, h)
  • rect(ten, y, w, h, r) - 'r' mean the radius in pixels to brand the corners rounded.
  • rect(x, y, w, h, tl, tr, br, bl) - Radius for top left, top correct, bottom correct, bottom left corners respectively. This is also in pixels.

ellipse() - For creating an ellipse shape. This is also used to create a circle, same width and superlative values should be given. The reference point for this shape is the center past default (refer to the effigy). Here is the usage:

  • ellipse(x, y, west, h)

arc() - Depict an arc. Usage:

  • arc(10, y, w, h, get-go, finish) - 'first' and 'finish' is used to determine the angle to kickoff and stop drawing the arc. Values are in radians. Constants such every bit "PI, HALF_PI, QUARTER_PI and TWO_PI" can be used.
  • arc(ten, y, due west, h, start, stop, mode) - 'mode' variable is to determine the rendering mode of the arc (string). Available options are "Open up, CHORD, PIE". OPEN will get out the not-drawn parts borderless. CHORD volition complete the not-drawn parts with a border. PIE will brand your arc await similar a pie nautical chart.

Displaying texts on the screen is similar to displaying shapes, the basic idea is that you lot make up one's mind a coordinate at which you want your text to be displayed. In that location is however more to handling texts. You will have more than command over your texts after the backdrop & settings department, where y'all'll acquire how to apply settings and properties to objects. For now, I volition prove the basics of displaying texts. In that location are many ways to exercise it, I'll but show the essentials.

text() - Brandish texts. Usage:

  • text(c, 10, y) - 'c' means character, any alphanumeric character volition be displayed.
  • text(c, x, y, z) - In case you are working with 3 dimensions.
  • text(str, x, y) - 'str' is the cord to exist displayed.
  • text(str, x, y, z) - In example you are working with iii dimensions.
  • text(num, x, y) - 'num' is the numeric value to be displayed.
  • text(num, x, y, z) - In example y'all are working with iii dimensions.

Properties & Settings

First thing that should be explained in this section would be the logic behind setting properties of objects. Fill color, background colour, border, border width, border color, alignment of the shapes, border styles etc. could be some examples of these properties.

When you set a property, yous take to recollect that the code will be executing from meridian to bottom. Say, y'all set the "fill up" property to scarlet, all the objects drawn below that line will be filled with red until it gets overwritten by another fill up property. Same thing applies for other properties equally well, nonetheless note that not all properties will overwrite each other. For example "stroke" belongings doesn't overwrite "fill" belongings, instead they work together. Here is a visual representation for you to comprehend the logic:

As you tin see in the image, get-go line sets the make full color to carmine and the second line sets the stroke color to bluish. We now have ii active settings: fill red and blue strokes. Every bit you'd expected, whatever our object may exist on the next line, it will be filled with cerise and have blue strokes (if applicable). You tin can keep examining the image this way, and you will grasp the logic.

Here are some essential properties & settings that are normally used:

Styling settings

fill() - Sets the fill color to objects. This setting is besides used to color texts. For now, we only demand to know the following usage:

  • fill(r, g, b) - Reddish, dark-green and blue values equally integer
  • fill up(r, g, b, a) - Boosted alpha value, max is 255

noFill() - Sets the fill color to transparent.

stroke() - Sets the stroke color to objects. Stroke belongings is applicative for lines and borders around objects. For now, nosotros only need to know the following usage:

  • stroke(r, g, b) - Red, greenish and blue values as integer.
  • stroke(r, g, b, a) - Additional blastoff value, max is 255

noStroke() - Removes the stroke.

strokeWeight() - Sets the width of the stroke. Usage:

  • strokeWeight(ten) - x is an integer and represents the width of stroke in pixels.

background() - Sets the groundwork color. For now, we only need to know the following usage:

  • groundwork(r, g, b) - Cherry-red, greenish and bluish values as integer.
  • background(r, g, b, a) - Additional alpha value, max is 255

Alignment Settings

ellipseMode() - Sets where to take as reference point aligning ellipses. Usage:

  • ellipseMode(manner) - 'way' is the parameter, here are the available parameters:
    • Middle (default): Take the center as the reference indicate.
    • RADIUS: This likewise takes the eye as a the reference point, but in this mode, the due west and h values y'all specify are treated as half (ie. radius instead of diameter)
    • CORNER: Takes top left corner as a reference point.
    • CORNERS: Sets the first two parameters (10 and y) as the location of the top-left corner, and last ii parameters (w and h) as the location of the lesser left corner of the ellipse. So this manner, "width" and "height" is irrelevant. Thinking it equally ellipse(x_tl,y_tl,x_br,y_br) makes more sense in this case.

rectMode() - Sets where to accept as reference point adjustment rectangles. Usage:

  • rectMode(mode) - 'mode' is the parameter, hither are the available parameters:
    • Centre: Have the center equally the reference signal.
    • RADIUS: This also takes the center every bit a the reference point, but in this mode, the west and h values yous specify are treated as one-half
    • CORNER (default): Takes meridian left corner as a reference point.
    • CORNERS: Sets the first two parameters (x and y) as the location of the top-left corner, and last 2 parameters (westward and h) every bit the location of the bottom left corner of the ellipse. So this mode, "width" and "height" is irrelevant. Thinking of it equally rect(x_tl,y_tl,x_br,y_br) makes more sense in this case.

textSize() - Sets the font size of text. Usage:

  • textSize(size) - Integer value of the desired size.

textLeading() - Sets the line height of your texts. Usage:

  • textLeading(lineheight) - Pixel value of the infinite betwixt lines.

textAlign() - Sets where to take equally reference point aligning texts. Usage.

  • textAlign(alignX) - 'alignX' is for horizontal alignment. Available: LEFT, Center, Right
  • textAlign(alignX, alignY) - 'alignY' is for vertical alignment. Available: TOP, BOTTOM, Middle, BASELINE.

Animations

Then far, we learned how to depict objects and texts. Merely the trouble with them is that they are static. Now how do we make them motility? Simple, instead of giving coordinates every bit integers, we utilize variables so that we can increment / decrement them. Make sense? Have a look at the following code:

          // initialize x and y as 0 int x=0; int y=0;  void setup(){   size(800,600);   background(255); // set groundwork color to white }  void draw(){   fill(255,0,0); // fill up color red   stroke(0,0,255); // stroke colour blue   ellipseMode(CENTER); // ref. point to ellipse is its center      ellipse(x, y, 20, 20); // draw the ellipse      // increment 10 and y   x+=5;   y+=5; }                  

Practise yous run across how nosotros managed the animation? We fix x and y as global variables and their initial value to 0. In our draw loop, nosotros created our ellipse, set the fill colour to red, stroke color to blueish and coordinates to x and y. When we increment 10 and y, the ball simply changes its location. Only there is a problem with this code, can you notice it? As an like shooting fish in a barrel challenge for yourself, try to figure what the trouble is, and test it out. Hither is the outcome:

My intention for letting this happen was to make you lot realise how the looping nature of Processing works. Refer to example at the "Describe Block" section, exercise you recall why nosotros got "one 1 1…" instead of "1 ii 3…" ? The same reason why the ball is leaving marks behind. Each time the draw block iterates, ten and y gets incremented by 5 and therefore the brawl gets redrawn to 5 pixels down and right. Nevertheless the ball is drawn from the previous iterations remain in the view. How do we make them go away? Any guesses?

To get rid of the marks the ball leaves behind, nosotros simply remove the groundwork(255) from setup cake, and paste it to be the very first line of the draw block. When our background lawmaking was in the setup block, it ran one time at the first, making our background white. Only that isn't enough, we need it to set our background to white on each loop to embrace the assurance fatigued from the previous loops. Groundwork existence the kickoff line ways it runs kickoff, it becomes the base of operations layer. On each loop, our canvas is painted white, and new elements gets drawn on top of the white background. So we take no marks.

That is the thought behind animative things in Processing, manipulating the objects' coordinates programmatically to change their location. Merely how will nosotros practice fancy stuff, such equally keeping the ball in the screen? Or maybe implementing gravity? I volition teach how to practise this stuff in side by side part of this article. We will learn by trying and building. Nosotros will learn how to exercise it and utilise them to our game immediately. At the end, we will take a consummate, playable, and hopefully fun game.

Keyboard & Mouse Interactions

Keyboard & mouse interactions in Processing are very like shooting fish in a barrel and straightforward. There are methods you tin call for each event, and what y'all write inside will be executed once when the event occurs. Also there are global variables such as mousePressed and keyPressed you lot tin can use in your draw block to have advantage of the loop. Here are some of the methods with explanations:

          void setup() {   size(500, 500); }  void draw() {    if (mousePressed) {     // Codes hither will be executed as long as the mouse     // button is pressed          if (mouseButton == LEFT){       // This lines volition be executed every bit long as       // the clicked mouse push is the left mouse       // button.     }   }    if (keyPressed) {     // Codes hither volition be executed every bit long every bit a key     // on the keyboard is pressed          if (key == CODED) {       // This if statement checks if the pressed fundamental       // is recognised past Processing.               if (keyCode == ENTER) {         // This lines will be executed if the pressed key         // is the enter primal.       }     }     else{       // This lines will be executed if the pressed key       // is not recognised by processing.     }   }    }  void mousePressed() {   // These codes will be executed in one case, when mouse   // is clicked. Note that mouseButton variable is   // as well be used here. }  void keyPressed() {   // These codes will be executed once, when a key   // is pressed. Note that key and keyCode variables   // are also usable here. }                  

As y'all can see, it is pretty piece of cake to check whether the mouse is clicked or which primal is existence pressed. There are however more options available for mousePressed and keyCode variables. Available options for mousePressed are LEFT, RIGHT and CENTER. There are many more bachelor for keyCode; UP, Downward, LEFT, RIGHT, ALT, CONTROL, SHIFT, BACKSPACE, TAB, ENTER, RETURN, ESC and DELETE.

1 affair to know about the mouse variables, and we volition use this a lot, is how to get the coordinates of the mouse. To become the verbal coordinates of the cursor, we can use mouseX and mouseY variables direct in the draw() block. Terminal merely non to the lowest degree, there are a lot of other useful methods that you should take a expect at. They are all documented in the Processing Reference.

Decision

Yous should exist getting familiar with Processing past now. Even so if you stop here, all this knowledge will fly abroad. I strongly recommend you lot continue practicing, playing around with what you have learned. To help you lot practice, I will provide you with ii exercises. You should try your all-time to do it on your own. If yous get stuck, Google and Processing Reference should exist your all-time friends. I will provide the code for the get-go i, but looking at them should be the final thing that you exercise.

You lot should brand 4 assurance with different colors, starting from 4 corners of the screen traveling through the centre with different speeds. When you lot click and hold the mouse push, the assurance should freeze. And when yous let go of the mouse, the balls could go back to their initial position and keep moving. So, I am looking for something like this.

After yous endeavour the exercise yourself, y'all may check out the lawmaking here.

Remember the famous DVD screensaver which the DVD logo bounces around the screen and we all waited desperately for it to hit the corner? I want you to replicate that screensaver, simply only using a rectangle instead of the DVD logo. When yous start the app, the screen should be black and the rectangle should showtime at a random location. Each time the rectangle hits the corner, it should change its color (and obviously management). When you move the mouse around, the rectangle should disappear and the groundwork colour should turn white (it is a screensaver, isn't it?). I will not give the lawmaking for this exercise in this article. You should attempt your all-time to implement it, and the code will exist provided in the second office of this article.

The second office of the ultimate guide to Processing, a stride-by-step tutorial to edifice a simple game, has been published.


Further Reading on the Toptal Engineering Blog:

  • How to Approach Writing an Interpreter From Scratch

Source: https://www.toptal.com/game/ultimate-guide-to-processing-the-fundamentals

Posted by: smithwich1999.blogspot.com

0 Response to "How To Draw A Heart In Processing"

Post a Comment

Iklan Atas Artikel

Iklan Tengah Artikel 1

Iklan Tengah Artikel 2

Iklan Bawah Artikel