[JAVA] Presentation slides made with Processing

Introduction

Hello, this is ohayota. Have you ever wanted to make a presentation slide with Processing (?) The other day, I made a slide for the stage at [Ryukyu University and Future University Joint LT] [1] with ** Processing and presented it with Processing. ** ** So, in this article, I'll write about how I made the slides. [1]:https://connpass.com/event/133298/

What you need to read this article

--Processing (Java) grammar understanding --Understanding object-oriented (classes, etc.) grammar

Image of the slide you want to make

The slide I'm trying to make here looks like the image below.

Cover slide

coverSlideImage.png ### Normal slide slideImage.png

We will implement it according to these two images. (The basic explanation of Processing is omitted. If you do not understand, please refer to [Official Reference] [2] etc.) [2]:https://processing.org/reference/

Constitution

Even if you say to make slides, it is troublesome to write rect (...) and text (...) one by one in setup () and draw (). Especially if you want to make dozens of slides, it's a hassle (at least I don't like it). Therefore, we prepared multiple classes and implemented them in ** object-oriented **.

The structure of the class was roughly decided as follows.

-** Slide ** (class representing one slide) -** TopBar ** (class representing the area at the top of the screen) -** TextField ** (a class that represents an area that displays text and images) -** Text ** (class representing the text to be displayed) -** CroppedImage ** (class that displays a circular image) -** Image ** (class that displays a rectangular image) -** BottomBar ** (class representing the area at the bottom of the screen) -** CroppedImage ** (class that displays a circular image)

One set of ** TopBar ** & ** TextField ** & ** BottomBar ** is required for each slide.

Implementation

We will implement it for each class. First of all, from the preparation.

Preparation

Before mounting, prepare the screen size, color, images and fonts to be used.

//Font to use
PFont yuGothic90;
PFont yuGothic70;
PFont yuGothic50;
PFont yuGothic30;
PFont yuGothic15;
PFont helvetica;
//Image to use
PImage dummy600;

// createSlides()Put all the slides in
ArrayList<Slide> slides = new ArrayList<Slide>();
String slideName = "Slide name";
int slideNum = 0;  //Display target slide number

boolean isKeyTyped = false;

final color backColor = color(240);
final color mainColor = color(0, 0, 70);
final color subColor1 = color(100);
final color subColor2 = color(200);

void setup() {
  fullScreen();
  noStroke();
  noCursor();
  imageMode(CENTER);
  frameRate(15);
  
  yuGothic90 = createFont("YuGo-Bold", 90, true);
  yuGothic70 = createFont("YuGo-Bold", 70, true);
  yuGothic50 = createFont("YuGo-Bold", 50, true);
  yuGothic30 = createFont("YuGo-Bold", 30, true);
  yuGothic15 = createFont("YuGo-Bold", 15, true);
  helvetica = createFont("Helvetica-Bold", 50, true);
  
  dummy600 = loadImage("dummy600.png ");
  
  createSlides();
}

void createSlides() {
  //Write slide addition process
}

Prepare the materials to be used. Use fillScreen () to display it in full screen. Use createSlides () once the Slide class is complete.

Prepare a dummy image of 600px * 600px for the program handled below. Create a directory called "data" in the same directory and save it as dummy600.png. dummy600.png

Now that we are ready, we will implement each class. From this point onward, the layout will be determined relatively ** using the vertical and horizontal sizes of the screen. ** **

TopBar class

slideTop.png

What you need is as follows.

--Navy blue background --Title --Subtitle --Slide number

Implement so that these can be displayed.

TopBar.pde


class TopBar {

  float barHeight;
  String title;
  String subTitle;
  int number;

  TopBar(float barHeight, String title, String subTitle, int number) {
    this.barHeight = barHeight;
    this.title = title;
    this.subTitle = subTitle;
    this.number = number;
  }

  void drawBase() {
    noStroke();
    fill(mainColor);
    rect(0, 0, width, barHeight);
  }

  void drawTitle() {
    fill(backColor);
    textFont(yuGothic70);
    textSize(height/17.5);
    textAlign(LEFT, CENTER);
    text(title, width/33.6, barHeight/2);
  }

  void drawSubTitle() {
    fill(backColor);
    textFont(yuGothic30);
    textSize(height/35);
    textAlign(RIGHT, CENTER);
    text(subTitle, width*10.2/11.2, barHeight/2);
  }

  void drawNumber() {
    fill(backColor);
    textFont(helvetica);
    textSize(height/21);
    textAlign(RIGHT, CENTER);
    text(number, width*32.6/33.6, barHeight/2);
  }

  void draw() {
    drawBase();
    drawTitle();
    drawSubTitle();
    drawNumber();
  }

}

Image class

imageSquare.png

As shown in the above example, it is a class that displays a single rectangular image.

Image.pde


class Image {
  
  PImage image;
  float sizeX;
  float sizeY;
  float x;
  float y;
  color back;
  boolean isReflect;  //Do you need to flip it upside down?
  
  Image(PImage image, float sizeX, float sizeY, float x, float y, color back, boolean isReflect) {
    this.image = image;
    this.sizeX = sizeX;
    this.sizeY = sizeY;
    this.x = x;
    this.y = y;
    this.back = back;
    this.isReflect = isReflect;
  }
  
  void drawImage() {
    pushMatrix();
    translate(x, y);
    if (isReflect) rotate(radians(180));
    image(image, 0, 0, sizeX, sizeY);
    popMatrix();
  }
  
  void drawFrame() {
    noFill();
    strokeWeight(sizeY/100+2);
    stroke(mainColor);
    rect(x-sizeX/2, y-sizeY/2, sizeX, sizeY);
  }
  
  void draw() {
    drawImage();
    drawFrame();
  }
  
}

CroppedImage class

cimage.png

In the implementation of this class, the method used in [Displaying a round image with Processing] [3] is used. As shown in the above example, this class displays a single image cut out in a circle. [3]:https://qiita.com/ohayota/items/e00e29b2a86a8143f258

CroppedImage.pde


class CroppedImage {
  
  PImage image;
  float size;
  float x;
  float y;
  color back;
  boolean isReflect;  //Do you need to flip it upside down?
  
  CroppedImage(PImage image, float size, float x, float y, color back, boolean isReflect) {
    this.image = image;
    this.size = size;
    this.x = x;
    this.y = y;
    this.back = back;
    this.isReflect = isReflect;
  }
  
  void drawBaseImage() {
    pushMatrix();
    translate(x, y);
    if (isReflect) rotate(radians(180));
    image(image, 0, 0, size, size);
    popMatrix();
  }
  
  //Cut out a circle inside a square
  void drawCroppedShape() {
    pushMatrix();
    translate(x, y);
    fill(back);
    noStroke();
    beginShape();
    //The outer frame of the figure
    vertex(-(size/2+1), -(size/2+1));
    vertex(size/2+1, -(size/2+1));
    vertex(size/2+1, size/2+1);
    vertex(-(size/2+1), size/2+1);
    //Drawing a shape to cut out
    beginContour();
    for (int i = 360; 0 < i; i--) {
      vertex(size/2 * cos(radians(i)), size/2 * sin(radians(i)));
    }
    endContour();
    endShape(CLOSE);
    popMatrix();
  }
  
  void drawFrame() {
    //Draw the frame of the cropped image
    noFill();
    strokeWeight(size/100+2);
    stroke(mainColor);
    ellipse(x, y, size, size);
  }
  
  //To be precise, instead of cropping the image itself, draw the cropped figure on top of the image.
  void draw() {
    drawBaseImage();
    drawCroppedShape();
    drawFrame();
  }
  
}

BottomBar class

slideBottom.png

What you need is as follows.

--Dark gray background --Slide name --Presenter icon and name

Implement so that these can be displayed.

BottomBar.pde


class BottomBar {
  
  float barHeight;
  String title;
  CroppedImage image;
  
  BottomBar(float barHeight, String title) {
    this.barHeight = barHeight;
    this.title = title;
    image = new CroppedImage(dummy600, barHeight*2/3, width*7.5/8.4, height-barHeight/2, subColor1, false);
  }
  
  void drawBase() {
    noStroke();
    fill(subColor1);
    rect(0, height-barHeight, width, barHeight);
  }
  
  void drawTitle() {
    fill(subColor2);
    textFont(yuGothic30);
    textSize(height/35);
    textAlign(LEFT, CENTER);
    text(title, width/33.6, height-barHeight/2);
  }
  
  void drawUserInfo() {
    textSize(height/42);
    text("User name", image.x + image.size/2 + width/168, image.y - barHeight/8);
    textFont(yuGothic15);
    textSize(height/70);
    text("User ID, etc.", image.x + image.size/2 + width/168, image.y + barHeight/6);
    image.draw();
  }
  
  void draw() {
    drawBase();
    drawTitle();
    drawUserInfo();
  }
  
}

TextField class

slideField.png

A class that represents an area for displaying text and images. Set the level from 1 to 3 for bullets. A circle marker is displayed on the second level, and a line marker is displayed on the third level.

TextField.pde


class TextField {
  
  float topBarHeight;
  float bottomBarHeight;
  float x;
  float y;
  float fieldWidth;
  float fieldHeight;
  ArrayList<CroppedImage> cImages;
  ArrayList<Image> images;
  ArrayList<Text> texts;
  
  TextField(float topBarHeight, float bottomBarHeight, float marginX, float marginY) {
    this.topBarHeight = topBarHeight;
    this.bottomBarHeight = bottomBarHeight;
    this.x = marginX;
    this.y = topBarHeight + marginY;
    fieldWidth = width - marginX*2;
    fieldHeight = height - (y+bottomBarHeight+marginY);
    cImages = new ArrayList<CroppedImage>();
    images = new ArrayList<Image>();
    texts = new ArrayList<Text>();
  }
  
  void drawImages() {
    for (CroppedImage img: cImages) img.draw();
    for (Image img: images) img.draw();
  }
  
  void drawTexts() {
    if (!texts.isEmpty()) {
      int textX = 0;
      int textY = 0;
      int beforeLevel = texts.get(0).level;
      for (int i = 0; i < texts.size(); i++) {
        Text t = texts.get(i);
        switch (t.level) {
          case 0:
            textX = 0;
            textY += height/17.5;
            break;
          case 1:
            textX = 0;
            t.draw(textX, textY);
            textY += height/15;
            break;
          case 2:
            if (beforeLevel == 3) {
              textX -= width/28;
            } else if (beforeLevel != 2) {
              textX += width/28;
            }
            t.draw(textX, textY);
            textY += height/17.5;
            break;
          case 3:
            if (beforeLevel != 3) textX += width/28;
            t.draw(textX, textY);
            textY += height/21;
            break;
          default:
        }
        beforeLevel = t.level;
      }
    }
  }
  
  void draw() {
    pushMatrix();
    //Since the position in the TextField is set, the origin is at the upper left of the TextField.(0, 0)To
    translate(x, y);
    drawTexts();
    drawImages();
    popMatrix();
  }
  
}

Text class

Text class used in TextField class. It has information such as text content, level, font, marker, and color.

Text.pde


class Text {
  
  String text;
  int level;
  
  Text(String text, int level) {
    this.text = text;
    this.level = level;
  }
  
  //A circle is displayed on the left side of the text for the second level, and a line is displayed as a marker for the third level.
  void drawMarker(float x, float y) {
    switch (level) {
      case 2:
        fill(mainColor);
        noStroke();
        ellipse(x-width/48, y+height/52.5, height/42, height/42);
        break;
      case 3:
        stroke(mainColor);
        strokeCap(SQUARE);
        strokeWeight(height/262.5);
        line(x-width/33.6, y+height/70, x-width/60, y+height/70);
        break;
      default:
        return;
    }
  }
  
  void drawText(float x, float y) {
    switch (level) {
      case 1:
        fill(mainColor);
        textFont(yuGothic50);
        textSize(height/21);
        break;
      case 2:
        fill(subColor1);
        textFont(yuGothic50);
        textSize(height/26.25);
        break;
      case 3:
        fill(subColor1);
        textFont(yuGothic30);
        textSize(height/35);
        break;
      default:
        return;
    }
    textAlign(LEFT, TOP);
    text(text, x, y);
  }
  
  void draw(float x, float y) {
    drawMarker(x, y);
    drawText(x, y);
  }
  
}

Slide class

A class that represents one slide.

Slide.pde


class Slide {
  
  TopBar topBar;
  BottomBar bottomBar;
  TextField textField;
  int number;  //Slide number
  boolean isCover;
  String title;
  String subTitle;
  
  Slide(boolean isCover, int number, String title, String subTitle) {
    topBar = new TopBar(height/8, title, subTitle, number);
    bottomBar = new BottomBar(height/12, slideName);
    textField = new TextField(topBar.barHeight, bottomBar.barHeight, width/21, height/17.5);
    this.isCover = isCover;
    this.number = number;
    this.title = title;
    this.subTitle = subTitle;
  }
  
  void drawTitle() {
    fill(mainColor);
    textFont(yuGothic90);
    textSize(height/11.7);
    textAlign(CENTER, TOP);
    text(title, width/2, height*4.5/13);
  }
  
  void drawSubTitle() {
    fill(subColor1);
    textFont(yuGothic50);
    textSize(height/21);
    textAlign(CENTER, TOP);
    text(subTitle, width/2, height*8.5/13);
  }
  
  void draw() {
    background(backColor);
    if (isCover) {
      drawTitle();
      drawSubTitle();
    } else {
      topBar.draw();
      bottomBar.draw();
      textField.draw();
    }
  }
  
}

Add slide

Now that we have implemented all the classes that make up a slide, we will create each page of the slide (an instance of the Slide class). At the stage of preparation, there was a function called createSlides (). Add a slide in this function. (This time it is dummy data)

void createSlides() {
  //Cover
  slides.add(new Slide(true, 0, "title", "サブtitle"));
  //1st slide
  Slide s1 = new Slide(false, 1, "title", "サブtitle");
  s1.textField.texts.add(new Text("1st level", 1));
  s1.textField.texts.add(new Text("Second level", 2));
  s1.textField.texts.add(new Text("3rd level", 3));
  s1.textField.texts.add(new Text("Second level", 2));
  s1.textField.texts.add(new Text("3rd level", 3));
  s1.textField.texts.add(new Text("", 0));  //Blank line
  s1.textField.texts.add(new Text("1st level", 1));
  s1.textField.texts.add(new Text("Second level", 2));
  s1.textField.texts.add(new Text("Second level", 2));
  s1.textField.texts.add(new Text("3rd level", 3));
  s1.textField.texts.add(new Text("3rd level", 3));
  s1.textField.cImages.add(new CroppedImage(dummy600, s1.textField.fieldWidth/3,
                                            s1.textField.x + s1.textField.fieldWidth*7/10,
                                            s1.textField.fieldHeight/2, backColor, false));
  slides.add(s1);
  //2nd slide
  Slide s2 = new Slide(false, 2, "title", "サブtitle");
  s2.textField.texts.add(new Text("1st level", 1));
  s2.textField.texts.add(new Text("Second level", 2));
  s2.textField.texts.add(new Text("3rd level", 3));
  s2.textField.texts.add(new Text("Second level", 2));
  s2.textField.texts.add(new Text("3rd level", 3));
  s2.textField.texts.add(new Text("", 0));  //Blank line
  s2.textField.texts.add(new Text("1st level", 1));
  s2.textField.texts.add(new Text("Second level", 2));
  s2.textField.texts.add(new Text("Second level", 2));
  s2.textField.texts.add(new Text("3rd level", 3));
  s2.textField.texts.add(new Text("3rd level", 3));
  s2.textField.images.add(new Image(dummy600, s2.textField.fieldWidth/3, s2.textField.fieldWidth/3,
                                    s2.textField.x + s2.textField.fieldWidth*7/10,
                                    s2.textField.fieldHeight/2, backColor, false));
  slides.add(s2);
  //3rd slide...
}

Create an instance of the slide and add an instance of text (** Text ) or image ( Image ** or ** Cropped Image **) to the textField inside that instance. After adding the textField, add an instance of the slide to ʻArrayList slides`.

Display slides

Now that the slide has been added, call draw () on the slide with draw ().

void draw() {
  background(backColor);
  slides.get(slideNum).draw();
}

The cover slide is now displayed.

Switching slides

In order to use it for presentations, you need a function that can switch slides. You can switch between them with the left and right arrows on the keyboard.

keyEvent.pde


void keyPressed() {
  if (!isKeyTyped) {
    switch (keyCode) {
      case LEFT:
        if (0 < slideNum) {
          loop();
          slideNum--;
        }
        break;
      case RIGHT:
        if (slideNum < slides.size()-1) {
          loop();
          slideNum++;
        }
        break;
      default:
    }
    isKeyTyped = true;
  }
}

void keyReleased() {
  isKeyTyped = false;
}

If you are looping when a key is pressed, you may switch between multiple slides with just one press. Therefore, add noLoop () to the draw () function.

void draw() {
  background(backColor);
  slides.get(slideNum).draw();

  noLoop();  //Avoid looping except when a specific key is pressed
}

When the arrow key is pressed once, it will switch one by one. This completes the implementation.

in conclusion

The slides implemented this time were my own slide master, but you can create original slides by customizing the colors and layout in the program. Please try it.

Recommended Posts

Presentation slides made with Processing
Christmas with Processing
Simple obstacle racing made with processing for Java
Getting Started with Doma-Annotation Processing
Spring with Kotorin --6 Asynchronous processing
Progress made with Swift UI
Asynchronous processing with Shoryuken + SQS
Unit catcher made by Processing
Pokemon Go made with Faker Gem
I made a GUI with Swing
NLP4J [006-031] 100 language processing knocks with NLP4J # 31 verb
Micro benchmark made with JFR API
Processing speed with and without static
Server processing with Java (Introduction part.1)
Run an application made with Java8 with Java6
Develop Processing with IntelliJ + Kotlin + Gradle
Image processing: Let's play with the image