home processing download documents tutorial python tutorial gallery source about
 Python 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".

add_library('igeo')

def setup() : 
    # ...


class MyRotatedModule(MyModule) : 
    # instance methods
    def method2(self): 
        # ...


class MyModule : 
    # ...
    def method1(self): 
        # ...

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.

add_library('igeo')

def setup() : 
    size(480, 360, IG.GL)
    
    module1 = MyRotatedModule(IVec(0,0,0), 20, PI/4)
    module2 = MyRotatedModule(IVec(50,0,0), PI/4)


class MyModule : 
    # static fields
    defaultSize = 10.0
    
    # static methods
    @staticmethod
    def randomXShift(pos, min, max) : 
        return pos.dup().add(IRand.get(min,max),0,0)
    
    @staticmethod
    def randomYShift(pos, min, max) : 
        return pos.dup().add(0,IRand.get(min,max),0)
    
    # instance methods
    def __init__(self, pos, sz = defaultSize) : 
        self.position = pos
        self.size = sz
    
    def createModule(self) : 
        return IBox(self.position, self.size)
    
    def createModuleWithArm(self, armSize, armLen) : 
        boxes = [] 
        boxes.append(IBox(self.position, self.size))
        armPos1 = MyModule.randomXShift(self.position, -(armLen-self.size), 0).add(0,0,self.size)
        boxes.append(IBox(armPos1, armLen, armSize, armSize))
        armPos2 = MyModule.randomXShift(armPos1, 0, armLen-self.size)
        armPos2 = MyModule.randomYShift(armPos2, -(armLen-self.size), 0).add(0,0,armSize)
        boxes.append(IBox(armPos2, armSize, armLen, armSize))
        return boxes


class MyRotatedModule(MyModule) : 
    # instance methods
    def __init__(self,pos, rotAngle, sz=MyModule.defaultSize) : 
        MyModule.__init__(self, pos, sz)
        self.rotationAngle = rotAngle


     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.

add_library('igeo')

def setup() : 
    size(480, 360, IG.GL)
    
    module1 = MyRotatedModule(IVec(0,0,0), PI/3, 20)
    box = module1.createModule()
    box.clr(1.0,0,0)

    module2 = MyRotatedModule(IVec(50,0,0), PI/4)
    boxes = module2.createModuleWithArm(5, 40)
    for bx in boxes : 
        bx.clr(0,0,1.0)

class MyModule : 
    # static fields
    defaultSize = 10.0
    
    # static methods
    @staticmethod
    def randomXShift(pos, min, max) : 
        return pos.dup().add(IRand.get(min,max),0,0)
    
    @staticmethod
    def randomYShift(pos, min, max) : 
        return pos.dup().add(0,IRand.get(min,max),0)
    
    # instance methods
    def __init__(self, pos, sz = defaultSize) : 
        self.position = pos
        self.size = sz
    
    def createModule(self) : 
        return IBox(self.position, self.size)
    
    def createModuleWithArm(self, armSize, armLen) : 
        boxes = [] 
        boxes.append(IBox(self.position, self.size))
        armPos1 = MyModule.randomXShift(self.position, -(armLen-self.size), 0).add(0,0,self.size)
        boxes.append(IBox(armPos1, armLen, armSize, armSize))
        armPos2 = MyModule.randomXShift(armPos1, 0, armLen-self.size)
        armPos2 = MyModule.randomYShift(armPos2, -(armLen-self.size), 0).add(0,0,armSize)
        boxes.append(IBox(armPos2, armSize, armLen, armSize))
        return boxes

class MyRotatedModule(MyModule) : 
    # static fields
    rotationAxis = IVec(0,0,1)
  
    # instance methods
    def __init__(self, pos, rotAngle, sz=MyModule.defaultSize) :
        MyModule.__init__(self, pos, sz)
        self.rotationAngle = rotAngle
  
    def createModule(self) : 
        box = MyModule.createModule(self)
        box.rot(self.position,self.rotationAxis,self.rotationAngle)
        return box
    
    def createModuleWithArm(self, armSize, armLen) : 
        boxes = MyModule.createModuleWithArm(self, armSize, armLen)
        for box in boxes : 
            box.rot(self.position, self.rotationAxis, self.rotationAngle)
        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.

add_library('igeo')

def setup() : 
    size(480, 360, IG.GL)
    
    IG.open("surface12.3dm")
    surfaces = IG.surfaces()
    unum=40
    vnum=40
    uinc=1.0/unum
    vinc=1.0/vnum
    for surf in surfaces : 
        for i in range(unum) : 
            for j in range(vnum) : 
                pt = surf.pt(i*uinc, j*vinc)
                module = MyRotatedModule(pt, i*uinc*PI, 1.5)
                
                if IRand.pct(50) :
                    box = module.createModule()
                    box.clr(0.7,0,0)
                else : 
                    armLength = IRand.get(2,10)
                    boxes = module.createModuleWithArm(0.5,armLength)
                    for bx in boxes : 
                        bx.clr(IRand.gray())
        surf.del()

class MyModule : 
    # static fields
    defaultSize = 10.0
    
    # static methods
    @staticmethod
    def randomXShift(pos, min, max) : 
        return pos.dup().add(IRand.get(min,max),0,0)
    
    @staticmethod
    def randomYShift(pos, min, max) : 
        return pos.dup().add(0,IRand.get(min,max),0)
    
    # instance methods
    def __init__(self, pos, sz = defaultSize) : 
        self.position = pos
        self.size = sz
    
    def createModule(self) : 
        return IBox(self.position, self.size)
    
    def createModuleWithArm(self, armSize, armLen) : 
        boxes = [] 
        boxes.append(IBox(self.position, self.size))
        armPos1 = MyModule.randomXShift(self.position, -(armLen-self.size), 0).add(0,0,self.size)
        boxes.append(IBox(armPos1, armLen, armSize, armSize))
        armPos2 = MyModule.randomXShift(armPos1, 0, armLen-self.size)
        armPos2 = MyModule.randomYShift(armPos2, -(armLen-self.size), 0).add(0,0,armSize)
        boxes.append(IBox(armPos2, armSize, armLen, armSize))
        return boxes


class MyRotatedModule(MyModule) : 
    # static fields
    rotationAxis = IVec(0,0,1)
  
    # instance methods
    def __init__(self, pos, rotAngle, sz=MyModule.defaultSize) :
        MyModule.__init__(self, pos, sz)
        self.rotationAngle = rotAngle
  
    def createModule(self) :
        box = MyModule.createModule(self)
        box.rot(self.position,self.rotationAxis,self.rotationAngle)
        return box
    
    def createModuleWithArm(self, armSize, armLen) :
        boxes = MyModule.createModuleWithArm(self, armSize, armLen)
        for box in boxes : 
            box.rot(self.position, self.rotationAxis, self.rotationAngle)
        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