Defining Functions for the SpaceClaim Wizard for Generating a BGA

The IronPython script main.py follows. This script defines all functions executed by the callbacks in the steps for the SpaceClaim wizard BGAWizard.

import System
import clr
import sys
import os
import math

part = None

def oninit(context):
    return

def createMyFeature(ag):
    ExtAPI.CreateFeature("MyFeature1")

def createSphere(x, y, z, radius):
    global part
    from System.Collections.Generic import List

    # get selected part
    if part==None:
        win = Window.ActiveWindow
        context = win.ActiveContext
        part = context.ActivePart.Master

    center = Geometry.Point.Create(x, y, z)
    profileFrame = Geometry.Frame.Create(center, Geometry.Direction.DirX, Geometry.Direction.DirY)
    sphereCircle = Geometry.Circle.Create(profileFrame, radius)
    sphereRevolveLine = Geometry.Line.Create(center, Geometry.Direction.DirX)
    profile = List[Geometry.ITrimmedCurve]()
    profile.Add(Geometry.CurveSegment.Create(sphereCircle, Geometry.Interval.Create(0, math.pi)));
    profile.Add(Geometry.CurveSegment.Create(sphereRevolveLine, Geometry.Interval.Create(-radius, radius)))
    path = List[Geometry.ITrimmedCurve]()
    sweepCircle = Geometry.Circle.Create(Geometry.Frame.Create(center, Geometry.Direction.DirY, Geometry.Direction.DirZ), radius)
    path.Add(Geometry.CurveSegment.Create(sweepCircle))
    body = Modeler.Body.SweepProfile(Geometry.Profile(Geometry.Plane.Create(profileFrame), profile), path)
    DesignBody.Create(part, "sphere", body)

def createCylinder(x, y, z, radius, h):
    global part
    from System.Collections.Generic import List

    # get selected part
    if part==None:
        win = Window.ActiveWindow
        context = win.ActiveContext
        part = context.ActivePart.Master

    defaultPointUV = Geometry.PointUV.Create(0, 0)
    profile = Geometry.CircleProfile(Geometry.Plane.PlaneXY, radius, defaultPointUV, 0)

    points = List[Geometry.Point]()
    points.Add(Geometry.Point.Create(0, 0, 0))
    points.Add(Geometry.Point.Create(0, 0, h))
    path = Geometry.PolygonProfile(Geometry.Plane.PlaneXY, points)

    body = Modeler.Body.SweepProfile(profile, path.Boundary)
    designBody = DesignBody.Create(part, "Cylinder", body)

    translation = Geometry.Matrix.CreateTranslation(Geometry.Vector.Create(x, y, z))
    designBody.Transform(translation)

def createCone(x, y, z, radius, h):
    global part
    from System.Collections.Generic import List

    # get selected part
    if part==None:
        win = Window.ActiveWindow
        context = win.ActiveContext
        part = context.ActivePart.Master

    defaultPointUV = Geometry.PointUV.Create(0, 0)
    path = Geometry.CircleProfile(Geometry.Plane.PlaneXY, radius, defaultPointUV, 0)

    points = List[Geometry.Point]()
    points.Add(Geometry.Point.Create(0, 0, 0))
    points.Add(Geometry.Point.Create(radius, 0, h))
    points.Add(Geometry.Point.Create(0, 0, h))
    triangle = Geometry.PolygonProfile(Geometry.Plane.PlaneZX, points)

    body = Modeler.Body.SweepProfile(triangle, path.Boundary)
    designBody = DesignBody.Create(part, "Cone", body)

    translation = Geometry.Matrix.CreateTranslation(Geometry.Vector.Create(x, y, z))
    designBody.Transform(translation)

def createBox(xa, ya, za, xb, yb, zb):
    global part
    # get selected part
    if part==None:
        win = Window.ActiveWindow
        context = win.ActiveContext
        part = context.ActivePart.Master

    lengthX = xb - xa
    lengthY = yb - ya
    lengthZ = zb - za
    xa = xa + lengthX * 0.5
    ya = ya + lengthY * 0.5

    p = Geometry.PointUV.Create(0, 0)
    body = Modeler.Body.ExtrudeProfile(Geometry.RectangleProfile(Geometry.Plane.PlaneXY, lengthX, lengthY, p, 0), lengthZ)
    designBody = DesignBody.Create(part, "body", body)

    translation = Geometry.Matrix.CreateTranslation(Geometry.Vector.Create(xa, ya, za))
    designBody.Transform(translation)

def createGear(x, y, z, innerRadius, outerRadius, width, count, holeRadius):
    global part
    from System.Collections.Generic import List

    # get selected part
    if part==None:
        win = Window.ActiveWindow
        context = win.ActiveContext
        part = context.ActivePart.Master
    frame = Geometry.Frame.World

    # create gear
    outsideCircle = Geometry.Circle.Create(frame, outerRadius);
    insideCircle  = Geometry.Circle.Create(frame, innerRadius);

    boundary = List[Geometry.ITrimmedCurve]()
    inwardLine = Geometry.Line.Create(frame.Origin, -frame.DirX);
    outwardLine = Geometry.Line.Create(frame.Origin, frame.DirX);
    axis = outsideCircle.Axis;

    nTeeth = count;
    repeatAngle = 2 * math.pi / nTeeth;
    toothAngle = 0.6 * repeatAngle;
    gapAngle = repeatAngle - toothAngle;

    for i in range(0, nTeeth):
        # an arc is just a parameter interval of a circle
        startTooth = i * repeatAngle;
        endTooth = startTooth + toothAngle;
        boundary.Add(Geometry.CurveSegment.Create(outsideCircle, Geometry.Interval.Create(startTooth, endTooth)));

        # rotate 'inwardLine' about the circle axis
        rotatedInwardLine = Geometry.Matrix.CreateRotation(axis, endTooth) * inwardLine;
        # a line segment is just a parameter interval of an unbounded line
        boundary.Add(Geometry.CurveSegment.Create(rotatedInwardLine, Geometry.Interval.Create(-outerRadius, -innerRadius)));

        startGap = endTooth;
        endGap = startGap + gapAngle;
        boundary.Add(Geometry.CurveSegment.Create(insideCircle, Geometry.Interval.Create(startGap, endGap)));

        rotatedOutwardLine = Geometry.Matrix.CreateRotation(axis, endGap) * outwardLine;
        boundary.Add(Geometry.CurveSegment.Create(rotatedOutwardLine, Geometry.Interval.Create(innerRadius, outerRadius)));

    hole = Geometry.Circle.Create(frame.Create(frame.Origin, frame.DirX, frame.DirY), holeRadius);
    boundary.Add(Geometry.CurveSegment.Create(hole));

    body = Modeler.Body.ExtrudeProfile(Geometry.Profile(Geometry.Plane.Create(frame), boundary), width);
    pieces = body.SeparatePieces().GetEnumerator()
    while pieces.MoveNext():
        designBody = DesignBody.Create(part, "GearBody", pieces.Current);

        translation = Geometry.Matrix.CreateTranslation(Geometry.Vector.Create(x, y, z))
        designBody.Transform(translation)

class Vector:
    def __init__(self, x = 0, y = 0, z = 0):
        self.x = x
        self.y = y
        self.z = z

    def Clone(self):
        return Vector(self.x, self.y, self.z)

    def NormSQ(self):
        return self.x*self.x + self.y*self.y + self.z*self.z

    def Norm(self):
        return math.sqrt(self.x*self.x + self.y*self.y + self.z*self.z)

    def Normalize(self):
        norm = self.Norm()
        self.x = self.x / norm
        self.y = self.y / norm
        self.z = self.z / norm

    def GetNormalize(self):
        norm = self.Norm(self)
        return Vector(self.x / norm, self.y / norm, self.z / norm)

    def __add__(va, vb):
        return Vector(va.x + vb.x, va.y + vb.y, va.z + vb.z)

    def __sub__(va, vb):
        return Vector(va.x - vb.x, va.y - vb.y, va.z - vb.z)

    def __mul__(v, x):
        return Vector(v.x*x, v.y*x, v.z*x)

    def Cross(va, vb):
        return Vector(va.y*vb.z - va.z*vb.z, -va.z*vb.x + va.x*vb.z, va.x*vb.y - va.y*vb.x)

    def Dot(va, vb):
        return va.x*vb.x + va.y*vb.y + va.z*vb.z

    def ToString(self):
        return "( " + str(self.x) + ", " + str(self.y) + ", " + str(self.z) + " )"

def CreateBalls(primitive, pitch, radius, column, row, supr, columnSupr, rowSupr, center, dirColumn, dirRow):
    dirColumn.Normalize()
    dirRow.Normalize()
    startVector = center - dirColumn*column*pitch*0.5 - dirRow*row*pitch*0.5
    startVector = startVector + dirColumn*radius + dirRow*radius
    startVector = startVector + dirRow.Cross(dirColumn)*radius
    stepVectorColumn = dirColumn * pitch
    stepVectorRow    = dirRow * pitch

    if(supr == "Yes"):
        column_index_to_start_supress = int( column * 0.5 - columnSupr * 0.5 )
        row_index_to_start_supress    = int( row    * 0.5 - rowSupr    * 0.5 )

    v = startVector.Clone()
    for i in range(column):
        for j in range(row):
            createBall = False
            if (supr == "Yes" and  (i <  column_index_to_start_supress or
                                    i >= column_index_to_start_supress + columnSupr or
                                    j <  row_index_to_start_supress or
                                    j >= row_index_to_start_supress+ rowSupr)
            or supr == "No"):
                if primitive == "sphere":
                    createSphere(v.x, v.y, v.z, radius)
                elif primitive == "cylinder":
                    createCylinder(v.x, v.y, v.z, radius, radius * 2.)
                elif primitive == "cone":
                    createCone(v.x, v.y, v.z, radius, radius * 2.)
                elif primitive == "cube":
                    createBox(v.x - radius, v.y - radius, v.z - radius,
                              v.x + radius, v.y + radius, v.z + radius)
                elif primitive == "gear":
                    createGear(v.x, v.y, v.z,
                               radius*0.5, radius, radius*2, 10, radius*0.2)
            v = v + stepVectorRow

        v = startVector.Clone()
        startVector = startVector + stepVectorColumn
        v = v + stepVectorColumn

def CreateDie(width, thickness, zStart):
    createBox(-0.5 * width, -0.5 * width, zStart,
              0.5 * width,  0.5 * width, zStart + thickness)

def CreateSubstrate(width, thickness, zStart):
    createBox(-0.5 * width, -0.5 * width, zStart,
               0.5 * width,  0.5 * width, zStart + thickness)

def CreateSolderMask(width, thickness, zStart):
    createBox(-0.5 * width, -0.5 * width, zStart,
               0.5 * width,  0.5 * width, zStart + thickness)

def generateBGAGeometry(feature,fct):
    ps = feature.Properties

    Pitch                            = ps["Solder Ball Details/Pitch"].Value
    Solder_Ball_Radius               = ps["Solder Ball Details/Solder Ball Radius"].Value
    No_Of_Solder_Ball_Column         = ps["Solder Ball Details/Number of Solder Ball Columns"].Value
    No_Of_Solder_Ball_Row            = ps["Solder Ball Details/Number of Solder Ball Rows"].Value
    No_Of_Solder_Ball_Column_Supress = ps["Central Balls/Central Thermal Balls/Number of Solder Ball Columns"].Value
    No_Of_Solder_Ball_Row_Supress    = ps["Central Balls/Central Thermal Balls/Number of Solder Ball Rows"].Value
    Substrate_Thickness              = ps["Substrate Details/Substrate Thickness"].Value
    Substrate_Width                  = ps["Substrate Details/Substrate Length"].Value
    Die_Thickness                    = ps["Die Details/Die Thickness"].Value
    Die_Width                        = ps["Die Details/Die Width"].Value
    Solder_Mask_Height               = ps["Solder Ball Details/Solder Mask Height"].Value
    supress_balls                    = ps["Central Balls/Central Thermal Balls"].Value
    ballsPrimitive                   = ps["BallsPrimitive"].Value

    bodies = []

    CreateBalls(ballsPrimitive, Pitch, Solder_Ball_Radius, No_Of_Solder_Ball_Column, No_Of_Solder_Ball_Row, supress_balls,
                No_Of_Solder_Ball_Column_Supress, No_Of_Solder_Ball_Row_Supress, 
                Vector(0, 0, 0), Vector(1, 0, 0), Vector(0, 1, 0))

    #Creating Substrate and soldermask
    CreateSubstrate(Substrate_Width, Substrate_Thickness, 0)
    CreateSolderMask(Substrate_Width, Solder_Mask_Height, 0)

    #Creating Die
    Die_Start = Substrate_Thickness
    CreateDie(Die_Width, Die_Thickness, Die_Start)

    return True
    
def GenerateDie(step):
    global part
    win = Window.ActiveWindow
    context = win.ActiveContext
    part = context.ActivePart
    
    ps = step.Properties
    Die_Thickness = ps["Thickness"].Value
    Die_Width     = ps["Width"].Value
    CreateDie(Die_Width, Die_Thickness, 0)
    
    part = None

def GenerateSubstrateAndSolderMask(step):
    global part
    win = Window.ActiveWindow
    context = win.ActiveContext
    part = context.ActivePart
    
    Die_Thickness = step.PreviousStep.Properties["Thickness"].Value

    ps = step.Properties
    Substrate_Thickness = ps["SubstrateDetails/Thickness"].Value
    Substrate_Width     = ps["SubstrateDetails/Length"].Value
    Solder_Mask_Height  = ps["SolderMaskDetails/Height"].Value

    CreateSubstrate(Substrate_Width, Substrate_Thickness, Die_Thickness)
    CreateSolderMask(Substrate_Width, Solder_Mask_Height, Die_Thickness + Substrate_Thickness)
    
    part = None

def GenerateBalls(step):
    global part
    win = Window.ActiveWindow
    context = win.ActiveContext
    part = context.ActivePart
    
    zStart = 0
    zStart += step.PreviousStep.PreviousStep.Properties["Thickness"].Value
    zStart += step.PreviousStep.Properties["SubstrateDetails/Thickness"].Value
    zStart += step.PreviousStep.Properties["SolderMaskDetails/Height"].Value

    ps = step.Properties
    faces      = ps["Face"].Value.Faces
    pitch      = ps["SolderBallDetails/Pitch"].Value
    radius     = ps["SolderBallDetails/Radius"].Value
    column     = ps["SolderBallDetails/Number of Solder Ball Columns"].Value
    row        = ps["SolderBallDetails/Number of Solder Ball Rows"].Value
    primitive  = ps["SolderBallDetails/BallsPrimitive"].Value
    columnSupr = ps["Central Balls/Central Thermal Balls/Number of Solder Ball Columns"].Value
    rowSupr    = ps["Central Balls/Central Thermal Balls/Number of Solder Ball Rows"].Value
    supr       = ps["Central Balls/Central Thermal Balls"].Value

    for i in range(0, faces.Count):
        face = faces[i]
        edges = face.Edges
        if edges.Count == 0:
            continue

        # find two edges with a comon point
        edgeA = edges[0]
        startPointA = edgeA.Shape.StartPoint
        endPointA   = edgeA.Shape.EndPoint
        for j in range(1, edges.Count):
            edgeB = edges[j]
            startPointB = edgeB.Shape.StartPoint
            endPointB   = edgeB.Shape.EndPoint

            if startPointB == startPointA:
                basePoint   = startPointB
                pointRow    = endPointA
                pointColumn = endPointB
            elif endPointB == startPointA:
                basePoint   = endPointB
                pointRow    = endPointA
                pointColumn = startPointB
            elif startPointB == endPointA:
                basePoint   = startPointB
                pointRow    = startPointA
                pointColumn = endPointB
            elif endPointB == endPointA:
                basePoint   = endPointB
                pointRow    = startPointA
                pointColumn = startPointB

            if not basePoint is None:
                dirColumn = Vector(pointRow.X - basePoint.X, pointRow.Y - basePoint.Y, pointRow.Z - basePoint.Z)
                dirRow    = Vector(pointColumn.X - basePoint.X, pointColumn.Y - basePoint.Y, pointColumn.Z - basePoint.Z)
                center    = Vector(basePoint.X, basePoint.Y, basePoint.Z) + (dirRow + dirColumn)*0.5
                CreateBalls(primitive, pitch, radius, column, row, supr, columnSupr, rowSupr, center, dirColumn, dirRow)
                break
                
    part = None