home processing download documents tutorial python tutorial gallery source about
 Tutorials (back to the list of tutorials)

Classes in Object-Oriented Programming

     Class Definition (requires iGeo version 7.3.1 or higher)

Object-oriented programming is one of paradigms of computer programming. An "object" is a structured collection of data and definition of its computational behavior. A template to define this structured collection of data and behavior is a "class". The code below is an example of code to define a class.

import igeo.*;

class MyModule{
  IVec position;
  double size;
}

This code above defines a class named "MyModule", containing several data. "class" is a keyword to define a class, followed by a name of the class. Usually name of a class starts with a capital letter.

In the following tutorial, some key topics of object-oriented programming are shown. For more comprehensive description about object-oriented programming, please see the Processing's tutorial and the Java tutorial.


     Class Instance

This code above is just defining the template and we need to write another code to create and use an object of the class.

import processing.opengl.*;
import igeo.*;

size(480, 360, IG.GL);

MyModule module = new MyModule();

The defined class is used as a type in declaration of a variable like "MyModule module" and an instance of the class is created by "new MyModule()".

However, the code above alone doesn't work by itself. Those two codes above need to be combined. Before combining, there is one thing to prepare. Here we introduce new programming mode "continuous mode" in Processing, whereas what we were writing so far was called "basic mode". See this page at processing.org for more information about programming modes in Processing.

The code above is rewritten in "continuous mode" below.

import processing.opengl.*;
import igeo.*;

void setup(){

  size(480, 360, IG.GL);

  MyModule module = new MyModule();

}

To write in "continuous mode", you need to put all the lines we were writing into "void setup(){" ... "}" except "import" statements.

Now we can combine the class definition with the main code and run the code in Processing without compile errors.

import processing.opengl.*;
import igeo.*;

void setup(){

  size(480, 360, IG.GL);

  MyModule module = new MyModule();
}

class MyModule{
  IVec position;
  double size;
}

When you run this code, it doesn't generate any geometry because we haven't define any behavior in the class yet.


     Instance Field

The class in the code above contains two variables in the class. Variables in a class is called fields. They are the members which the class consists of.

You can access to the fields to read a value or to assign a value to by writing "." and the name of the field after the instance. See the example below.

import processing.opengl.*;
import igeo.*;

void setup(){

  size(480, 360, IG.GL);

  MyModule module = new MyModule();
  module.position = new IVec(0,0,0);
  module.size = 20.0;

  IG.p("size of module is "+module.size);
}

class MyModule{
  // instance fields
  IVec position;
  double size;
}

     Class Constructor

To assign values to instance fields, it is easier to define a constructor. A constructor is a method to instantiate a class. It is used when an instance of the class is created with a keyword "new" and you can put values to initialize instance fields in a constructor. The name of a constructor method is exactly same with the class name. See the example below.

import processing.opengl.*;
import igeo.*;

void setup(){

  size(480, 360, IG.GL);

  MyModule module = new MyModule(new IVec(0,0,0), 20);
}

class MyModule{
  // instance fields
  IVec position;
  double size;

  // instance methods
  MyModule(IVec pos, double sz){
    position = pos;
    size = sz;
  }
}

You can also add multiple constructors with different input arguments. When you use one of constructors, you put a specific set of types of variables in the input arguments of the constructor.

import processing.opengl.*;
import igeo.*;

void setup(){

  size(480, 360, IG.GL);

  MyModule module1 = new MyModule(new IVec(0,0,0), 20);
  MyModule module2 = new MyModule(new IVec(50,0,0));
}

class MyModule{
  // instance fields
  IVec position;
  double size;

  // instance methods
  MyModule(IVec pos, double sz){
    position = pos;
    size = sz;
  }

  MyModule(IVec pos){
    position = pos;
    size = 1.0; // default size
  }
}

     Instance Method

An instance method is a procedure containing a series of statements to define a behavior of a class. A method can take any number of input arguments and one or no return value. You can name a method with any name but usually it starts with a small letter. Inside an instance method, you have full access to instance fields. The following is an example of an instance method with no input argument and no return value.

import processing.opengl.*;
import igeo.*;

void setup(){

  size(480, 360, IG.GL);

  MyModule module1 = new MyModule(new IVec(0,0,0), 20);
  module1.createModule();

  MyModule module2 = new MyModule(new IVec(50,0,0));
  module2.createModule();
}

class MyModule{
  // instance fields
  IVec position;
  double size;

  // instance methods
  MyModule(IVec pos, double sz){
    position = pos;
    size = sz;
  }

  MyModule(IVec pos){
    position = pos;
    size = 10.0;
  }

  void createModule(){
    new IBox(position, size);
  }
}

In this example code, name of the method is "createModule", preceded by a keyword "void" which specifies that this method has no return value, and followed by "()" to specify that this method has no input argument. This method is actually executed inside "setup()" method at the line of "module1.createModule();" for the instance "module1" and "module2.createModule();" for another instance "module2".

The following code is an example of an instance method with input arguments.

import processing.opengl.*;
import igeo.*;

void setup(){

  size(480, 360, IG.GL);

  MyModule module1 = new MyModule(new IVec(0,0,0), 20);
  module1.createModule();

  MyModule module2 = new MyModule(new IVec(50,0,0));
  module2.createModuleWithArm(5, 40);
}

class MyModule{
  // instance fields
  IVec position;
  double size;

  // instance methods
  MyModule(IVec pos, double sz){
    position = pos;
    size = sz;
  }

  MyModule(IVec pos){
    position = pos;
    size = 10.0; // default size
  }

  void createModule(){
    new IBox(position, size);
  }

  void createModuleWithArm(double armSize, double armLen){
    new IBox(position, size);

    IVec armPos1 = position.dup().add(-armLen/2,0,0);
    armPos1.add(0,0,size);
    new IBox(armPos1, armLen, armSize, armSize);

    IVec armPos2 = armPos1.dup().add(armLen/2,-armLen/2,0);
    armPos2.add(0,0,armSize);
    new IBox(armPos2, armSize, armLen, armSize);
  }
}

To set up input arguments, you declare a variable inside the parenthesis "()" after the method name and the declared variables can be used inside the method as input values passed from outside of the method.


     Method with A Return Value

A method can have an output value in the form of a return value. The following code is an example to define return values of methods.

import processing.opengl.*;
import igeo.*;

void setup(){

  size(480, 360, IG.GL);

  MyModule module1 = new MyModule(new IVec(0,0,0), 20);
  IBox box = module1.createModule();
  box.clr(1.0,0,0);

  MyModule module2 = new MyModule(new IVec(50,0,0));
  IBox[] boxes = module2.createModuleWithArm(5, 40);
  for(IBox bx : boxes){ bx.clr(0,0,1.0); }
}

class MyModule{
  // instance fields
  IVec position;
  double size;

  // instance methods
  MyModule(IVec pos, double sz){
    position = pos;
    size = sz;
  }

  MyModule(IVec pos){
    position = pos;
    size = 10.0; // default size
  }

  IBox createModule(){
    return new IBox(position, size);
  }

  IBox[] createModuleWithArm(double armSize, double armLen){
    IBox[] boxes = new IBox[3];
    boxes[0] = new IBox(position, size);

    IVec armPos1 = position.dup().add(-armLen/2, 0, 0);
    armPos1.add(0,0,size);
    boxes[1] = new IBox(armPos1, armLen, armSize, armSize);

    IVec armPos2 = armPos1.dup().add(armLen/2, -armLen/2, 0);
    armPos2.add(0,0,armSize);
    boxes[2] = new IBox(armPos2, armSize, armLen, armSize);
    return boxes;
  }
}

To define a return value of a method, you first put the type of return value before the method name, instead of "void". Then inside the body of the method, you add a return statement with the keyword "return" and the variable containing the data you want to output at the end of the body.


     Static Field

A static field is a type of field which is not inside each class instance but outside of it and is shared by all instances of the class. A static field has a keyword "static" in the declaration of a field.

import processing.opengl.*;
import igeo.*;

void setup(){

  size(480, 360, IG.GL);

  MyModule module1 = new MyModule(new IVec(0,0,0), 20);
  IBox box = module1.createModule();
  box.clr(1.0,0,0);

  MyModule module2 = new MyModule(new IVec(50,0,0));
  IBox[] boxes = module2.createModuleWithArm(5, 40);
  for(IBox bx : boxes){ bx.clr(0,0,1.0); }

  IG.p("default size is " + MyModule.defaultSize);
}

static class MyModule{
  // static fields
  static double defaultSize = 10.0;

  // instance fields
  IVec position;
  double size;

  // instance methods
  MyModule(IVec pos, double sz){
    position = pos;
    size = sz;
  }

  MyModule(IVec pos){
    position = pos;
    size = defaultSize;
  }

  IBox createModule(){
    return new IBox(position, size);
  }

  IBox[] createModuleWithArm(double armSize, double armLen){
    IBox[] boxes = new IBox[3];
    boxes[0] = new IBox(position, size);

    IVec armPos1 = position.dup().add(-armLen/2, 0, 0);
    armPos1.add(0,0,size);
    boxes[1] = new IBox(armPos1, armLen, armSize, armSize);

    IVec armPos2 = armPos1.dup().add(armLen/2, -armLen/2, 0);
    armPos2.add(0,0,armSize);
    boxes[2] = new IBox(armPos2, armSize, armLen, armSize);
    return boxes;
  }
}

Inside the class you can access to static fields in the same way with instance fields but when you change the value, the change is reflected on all class instances which read the static field. Outside the class you can access to static fields by putting the class name and "." before the name of the field like "MyModule.defaultSize".

One detailed technical issue which beginners can ignore is the "static" keyword at the beginning of the class definition "class MyModule{". This issue happens when you write classes in a same file in Processing. In "continuous mode" in Processing, there is one thing hidden for convenience, which is the class definition of the sketch itself. Internally the whole sketch is inside a sketch class (this shows up in "Java mode") and classes written inside the same sketch are treated as inner classes inside the sketch class. To use static fields or static methods, an inner class needs to be a "static class". That's why there's the keyword "static" at the beginning of the class definition.

You can write classes outside of the sketch class if you create separate files with the file extension of ".java" in new tabs in Processing. For more information to write separate Java files in Processing, please see this page at processing.org.


     Static Method

A static method is a method which doesn't belong to each instance but is shared by all instances of the class. A static method also has a keyword "static" in the declaration of the method.

import processing.opengl.*;
import igeo.*;

void setup(){

  size(480, 360, IG.GL);

  MyModule module1 = new MyModule(new IVec(0,0,0), 20);
  IBox box = module1.createModule();
  box.clr(1.0,0,0);

  MyModule module2 = new MyModule(new IVec(50,0,0));
  IBox[] boxes = module2.createModuleWithArm(5, 40);
  for(IBox bx : boxes){ bx.clr(0,0,1.0); }
}

static class MyModule{
  // static fields
  static double defaultSize = 10.0;

  // instance fields
  IVec position;
  double size;

  // static methods
  static IVec randomXShift(IVec pos, double min, double max){
    IVec shifted = pos.dup();
    shifted.add(IRandom.get(min,max),0,0);
    return shifted;
  }

  static IVec randomYShift(IVec pos, double min, double max){
    IVec shifted = pos.dup();
    shifted.add(0,IRandom.get(min,max),0);
    return shifted;
  }

  // instance methods
  MyModule(IVec pos, double sz){
    position = pos;
    size = sz;
  }

  MyModule(IVec pos){
    position = pos;
    size = defaultSize;
  }

  IBox createModule(){
    return new IBox(position, size);
  }

  IBox[] createModuleWithArm(double armSize, double armLen){
    IBox[] boxes = new IBox[3];
    boxes[0] = new IBox(position, size);

    IVec armPos1 = randomXShift(position, -(armLen-size), 0);
    armPos1.add(0,0,size);
    boxes[1] = new IBox(armPos1, armLen, armSize, armSize);

    IVec armPos2 = randomXShift(armPos1, 0, armLen-size);
    armPos2 = randomYShift(armPos2, -(armLen-size), 0);
    armPos2.add(0,0,armSize);
    boxes[2] = new IBox(armPos2, armSize, armLen, armSize);
    return boxes;
  }
}


     Use of Class in Other Algorithms

Here is an example to use a class in the panelization algorithms we've seen. The sample input file used in the code is this.

surface12.3dm

import processing.opengl.*;
import igeo.*;

void setup(){

  size(480, 360, IG.GL);

  IG.open("surface12.3dm");
  ISurface[] surfaces = IG.surfaces();

  int unum=40, vnum=40;
  double uinc=1.0/unum, vinc=1.0/vnum;
  for(ISurface surf : surfaces){
    for(int i=0; i < unum; i++){
      for(int j=0; j < vnum; j++){
        IVec pt = surf.pt(i*uinc, j*vinc);
        MyModule module = new MyModule(pt, 1.5);

        if(IRandom.percent(50)){
          IBox box = module.createModule();
          box.clr(0.7,0,0);
        }
        else{
          double armLength = IRandom.get(2,10);
          IBox[] boxes = module.createModuleWithArm(0.5,armLength);
          for(IBox bx:boxes){ bx.clr(IRandom.gray()); }
        }
      }
    }
    surf.del();
  }
}

static class MyModule{
  // static fields
  static double defaultSize = 10.0;

  // instance fields
  IVec position;
  double size;

  // static methods
  static IVec randomXShift(IVec pos, double min, double max){
    IVec shifted = pos.dup();
    shifted.add(IRandom.get(min,max),0,0);
    return shifted;
  }

  static IVec randomYShift(IVec pos, double min, double max){
    IVec shifted = pos.dup();
    shifted.add(0,IRandom.get(min,max),0);
    return shifted;
  }

  // instance methods
  MyModule(IVec pos, double sz){
    position = pos;
    size = sz;
  }

  MyModule(IVec pos){
    position = pos;
    size = defaultSize;
  }

  IBox createModule(){
    return new IBox(position, size);
  }

  IBox[] createModuleWithArm(double armSize, double armLen){
    IBox[] boxes = new IBox[3];
    boxes[0] = new IBox(position, size);

    IVec armPos1 = randomXShift(position, -(armLen-size), 0);
    armPos1.add(0,0,size);
    boxes[1] = new IBox(armPos1, armLen, armSize, armSize);

    IVec armPos2 = randomXShift(armPos1, 0, armLen-size);
    armPos2 = randomYShift(armPos2, -(armLen-size), 0);
    armPos2.add(0,0,armSize);
    boxes[2] = new IBox(armPos2, armSize, armLen, armSize);
    return boxes;
  }
}


     Comparison of Code with/without Class

As an example to use a class, the codes below show comparison of a panelization algorithm written without and with a class. The panelization algorithm is based on the triangular panelization on this tutorial.

surface1.3dm

import processing.opengl.*;
import igeo.*;

size( 480, 360, IG.GL );

IG.open("surface1.3dm");  //input geometry from 3dm file

ISurface[] surfs = IG.surfaces();

for( ISurface surf : surfs ){

  int unum = 8, vnum = 8;
  double uinc = 1.0/unum, vinc = 1.0/vnum;

  for(int i=0; i < unum; i++){
    for(int j=0; j < vnum; j++){
      IVec pt11 = surf.pt( i*uinc, j*vinc );
      IVec pt21 = surf.pt( (i+1)*uinc, j*vinc );
      IVec pt12 = surf.pt( i*uinc, (j+1)*vinc );
      IVec pt22 = surf.pt( (i+1)*uinc, (j+1)*vinc );
      new ISurface(pt11,pt21,pt22).clr(i*uinc,0,j*vinc);
      new ISurface(pt22,pt12,pt11).clr(1-i*uinc,1-j*vinc,1);
      
      double depth = -2;
      IVec spt11 = surf.pt(i*uinc, j*vinc, depth);
      IVec spt21 = surf.pt((i+1)*uinc, j*vinc, depth);
      IVec spt12 = surf.pt(i*uinc, (j+1)*vinc, depth);
      IVec spt22 = surf.pt((i+1)*uinc, (j+1)*vinc, depth);
      
      double frameSize = 0.2;
      IG.squarePipe(new IVec[]{spt11, spt21, spt22, spt12},
                    1, true, frameSize);

      double radius = 0.1;
      new ICylinder(pt11,spt11,radius);
      new ICylinder(pt21,spt21,radius);
      new ICylinder(pt12,spt12,radius);
      new ICylinder(pt22,spt22,radius);
    }
  }
  surf.del();
}

The code above is re-written with a class "MyPanel" below.
Note that the code below creates frames overlapping with adjacent frames due to the simplicity of the tutorial code.

import processing.opengl.*;
import igeo.*;

void setup(){

  size( 480, 360, IG.GL );
  IG.open("surface1.3dm");  //input geometry from 3dm file
  ISurface[] surfs = IG.surfaces();

  for( ISurface surf : surfs ){
    int unum = 8, vnum = 8;
    double uinc = 1.0/unum, vinc = 1.0/vnum;

    for(int i=0; i < unum; i++){
      for(int j=0; j < vnum; j++){
        double depth = -2;

        MyPanel panel =
          new MyPanel(surf.pt(i*uinc, j*vinc),
                      surf.pt((i+1)*uinc, j*vinc),
                      surf.pt(i*uinc, (j+1)*vinc),
                      surf.pt((i+1)*uinc, (j+1)*vinc),
                      surf.pt(i*uinc, j*vinc, depth),
                      surf.pt((i+1)*uinc, j*vinc, depth),
                      surf.pt(i*uinc, (j+1)*vinc, depth),
                      surf.pt((i+1)*uinc,(j+1)*vinc,depth));
         
         panel.createPanel(i*uinc, 0, j*vinc,
                           1-i*uinc, 1-j*vinc, 1);
      }
    }
    surf.del();
  }
}

static class MyPanel{
  // static field
  static double frameSize = 0.2;
  static double connectionRadius = 0.1;

  // instance field
  IVec pt11,pt21,pt12,pt22;
  IVec spt11,spt21,spt12,spt22;
  
  MyPanel(IVec p11, IVec p21, IVec p12, IVec p22,
          IVec s11, IVec s21, IVec s12, IVec s22){
    pt11 = p11;
    pt21 = p21;
    pt12 = p12;
    pt22 = p22;
    spt11 = s11;
    spt21 = s21;
    spt12 = s12;
    spt22 = s22;
  }

  void createPanel(double red1, double green1, double blue1,
                   double red2, double green2, double blue2){

    new ISurface(pt11,pt21,pt22).clr(red1,green1,blue1);
    new ISurface(pt22,pt12,pt11).clr(red2,green2,blue2);

    IG.squarePipe(new IVec[]{ spt11,spt21,spt22,spt12 },
                  1, true, frameSize);

    new ICylinder(pt11,spt11,connectionRadius);
    new ICylinder(pt21,spt21,connectionRadius);
    new ICylinder(pt12,spt12,connectionRadius);
    new ICylinder(pt22,spt22,connectionRadius);
  }
}

(back to the list of tutorials)

HOME
FOR PROCESSING
DOWNLOAD
DOCUMENTS
TUTORIALS (Java / Python)
GALLERY
SOURCE CODE(GitHub)
ABOUT