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

Class Inheritance (requires iGeo version 7.3.1 or higher)

     Subclass

Class inheritance is one of important concepts in object-oriented programming. You can create a subclass inheriting a superclass. A subclass and a superclass are also called a child class and a parent class. When a subclass inherit a superclass, a subclass contains and has access to all fields and methods of the superclass. On top of it, a subclass can add new fields and methods. By creating a subclass, you can expand functionality and behavior of the superclass.

The following code is an example of creating a subclass. A keyword to define a subclass is "extends".

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

void setup(){
  // ...
}

static class MyRotatedModule extends MyModule{
  // instance fields
  double rotationAngle;

}

static class MyModule{
  // ...
}

This code above defines a subclass named "MyRotatedModule", inheriting the superclass "MyModule". (This code skips the "setup()" method). The inheritance relationship is defined at the line of the class definition of the subclass, by adding "extends MyModule" after the class name of "MyRotatedModule". This subclass only adds one instance field of "double rotationAngle". This subclass doesn't have constructors yet. Constructors of subclass are described in the next section.


     Constructor of Subclass

The function of a constructor is to initialize internal data of a class. In a constructor of a subclass, this process has two steps. First, initialize its superclass and then secondly initialize the subclass itself. This first part is done inside a subclass's constructor by calling a constructor of the superclass with a keyword "super" in the first line. Calling a specific constructor of the superclass out of multiple constructors is done by putting a specific set of types in its arguments. If you don't call superclass's constructor with "super", superclass's default constructor "super()" (constructor with no argument) is called automatically. However to do this, you need to define the default constructor in the superclass (or if you define no constructor, a default constructor is defined automatically).

An example of constructors of a subclass is below.

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

void setup(){
  size(480, 360, IG.GL);

  MyRotatedModule module1 = 
    new MyRotatedModule(new IVec(0,0,0), 20, Math.PI/4);

  MyRotatedModule module2 = 
    new MyRotatedModule(new IVec(50,0,0), Math.PI/4);
}

static class MyRotatedModule extends MyModule{
  // instance fields
  double rotationAngle;

  // instance methods
  MyRotatedModule(IVec pos, double sz, double rotAngle){
    super(pos, sz);
    rotationAngle = rotAngle;
  }

  MyRotatedModule(IVec pos, double rotAngle){
    super(pos);
    rotationAngle = rotAngle;
  }
}

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){
    return pos.dup().add(IRandom.get(min,max),0,0);
  }

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

  // 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).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).add(0,0,armSize);
    boxes[2] = new IBox(armPos2, armSize, armLen, armSize);
    return boxes;
  }
}

     Overriding Methods

When you create a subclass, you can add new methods in addition to methods of its superclass and you can also override superclass's existing methods. To override a superlcass's method, you define a method with the exactly same name, arguments and return type. When you override a method, the original method in the superclass is hidden but you can still access to overridden superclass's method from inside of the subclass by adding the keyword "super" before the method name.

Actually it's natural to override a method by calling the same method in superclass and adding more codes to expand the behavior of the method because subclassing is conceptually expanding functionality. By overriding methods, behaviors of a class with same names of methods can be varied among classes having inheritance relationship. This feature of object-oriented programming is called "polymorphism".

The code below shows an example of overriding methods of the superclass.

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

void setup(){
  size(480, 360, IG.GL);

  MyRotatedModule module1 = new MyRotatedModule(new IVec(0,0,0), 20, PI/3);
  IBox box = module1.createModule();
  box.clr(1.0,0,0);

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

static class MyRotatedModule extends MyModule{
  // static fields
  static IVec rotationAxis = new IVec(0,0,1);
  // instance fields
  double rotationAngle;

  // instance methods
  MyRotatedModule(IVec pos, double sz, double rotAngle){
    super(pos, sz);
    rotationAngle = rotAngle;
  }

  MyRotatedModule(IVec pos, double rotAngle){
    super(pos);
    rotationAngle = rotAngle;
  }

  IBox createModule(){
    IBox box = super.createModule();
    box.rot(position, rotationAxis, rotationAngle);
    return box;
  }

  IBox[] createModuleWithArm(double armSize, double armLen){
    IBox[] boxes=super.createModuleWithArm(armSize, armLen);
    for(IBox box : boxes){
      box.rot(position, rotationAxis, rotationAngle);
    }
    return boxes;
  }
}

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){
    return pos.dup().add(IRandom.get(min,max),0,0);
  }

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

  // 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).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).add(0,0,armSize);
    boxes[2] = new IBox(armPos2, armSize, armLen, armSize);
    return boxes;
  }
}


     Use of Subclass

The code of panelization with a class in the previous section can be rewritten with the subclass as the following. You can see the change in the panelization algorithm inside "setup()" method is only one line instantiating "MyRotatedModule" class. Because you can assign an instance of a subclass to a variable of its superclass, other part of the algorithm doesn't need to change.

surface12.3dm is the input file used in the code.

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 MyRotatedModule(pt, 1.5, i*uinc*PI);

        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 MyRotatedModule extends MyModule{
  // static fields
  static IVec rotationAxis = new IVec(0,0,1);
  // instance fields
  double rotationAngle;

  // instance methods
  MyRotatedModule(IVec pos, double sz, double rotAngle){
    super(pos, sz);
    rotationAngle = rotAngle;
  }

  MyRotatedModule(IVec pos, double rotAngle){
    super(pos);
    rotationAngle = rotAngle;
  }

  IBox createModule(){
    IBox box = super.createModule();
    box.rot(position, rotationAxis, rotationAngle);
    return box;
  }

  IBox[] createModuleWithArm(double armSize, double armLen){
    IBox[] boxes=super.createModuleWithArm(armSize, armLen);
    for(IBox box : boxes){
      box.rot(position, rotationAxis, rotationAngle);
    }
    return boxes;
  }
}

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;
  }
}

For more description about class inheritance, please see Java's tutorial page on inheritance.


(back to the list of tutorials)

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