Plugin for Tekla - Part II
2017/01/10   Valts Silaputniņš, Raivis Spēlmanis

Second part in series describing development of plugin for Tekla Structures. Today we are going to examine how to make actual groove in the surface of selected panel. For more information about plugin's architecture see the first part. Please note that the follwing code is of illustrative purposes only, production code is much more robust.

Switching to local coordinates

This is a defintion of our method which will make a groove into the user's selected panel surface. This method is called from plugin's main Run method.

private void AddGroove(SurfaceObject pickedSurface) { //...

First we need to get points (vertices) that make the selected surface polygon. At first approximation we assume it has only 4 vertices and it is a regular polygon:

            Point surfacePoint1 = new Point(pickedSurface.Polymesh.Vertices[0].X, pickedSurface.Polymesh.Vertices[0].Y, pickedSurface.Polymesh.Vertices[0].Z);
            Point surfacePoint2 = new Point(pickedSurface.Polymesh.Vertices[1].X, pickedSurface.Polymesh.Vertices[1].Y, pickedSurface.Polymesh.Vertices[1].Z);
            Point surfacePoint3 = new Point(pickedSurface.Polymesh.Vertices[2].X, pickedSurface.Polymesh.Vertices[2].Y, pickedSurface.Polymesh.Vertices[2].Z);
            Point surfacePoint4 = new Point(pickedSurface.Polymesh.Vertices[3].X, pickedSurface.Polymesh.Vertices[3].Y, pickedSurface.Polymesh.Vertices[3].Z);

Now with the points we got we can calculate X and Y vectors that belong to same plane as the surface. With these orthogonal vectors we can now build local CoordinateSystem based on selected surface.

            Vector surfaceXaxis = new Vector(surfacePoint2.X - surfacePoint1.X, surfacePoint2.Y - surfacePoint1.Y, surfacePoint2.Z - surfacePoint1.Z);
            Vector surfaceYaxis = new Vector(surfacePoint3.X - surfacePoint1.X, surfacePoint3.Y - surfacePoint1.Y, surfacePoint3.Z - surfacePoint1.Z);
            CoordinateSystem surfaceCoordSystem = new CoordinateSystem(surfacePoint1, surfaceXaxis, surfaceYaxis);

The trickiest part

And here comes the trickiest part. It's not guaranteed which way the local Z axis goes. It's not guaranteed to "go away" from the selected panel, but we always want to cut our groove inside the panel. We expect consistent behaviour - by setting negative local Z, our groove will always fit inside panel, below selected edge. This can be solved by checking if the point that is located at (0,0,1) in local coordinates resides inside panel or outside it. First of all we make a transformation matrix from local to global coordinates and then transform required point coordinates to global coordinate system.

            Matrix TransformationMatrixGlobal2Local = MatrixFactory.FromCoordinateSystem(surfaceCoordSystem);
            Point TestPoint = TransformationMatrixGlobal2Local.Transform(new Point(0, 0, 1));

The new TestPoint we got, we can pass to helper function that will check if the point is inside bounding box of the selected panel or not. After which we remake coordinate system so the local coordinate system's Z axis is "going away" from the panel's surface.

    /// Helper function that checks if point is inside object's bounding box or not
    private bool InsideObject(Part obj, Point point)
       Point point1 = new Point(obj.GetSolid().MinimumPoint.X, obj.GetSolid().MinimumPoint.Y, obj.GetSolid().MinimumPoint.Z);
       Point point2 = new Point(obj.GetSolid().MaximumPoint.X, obj.GetSolid().MaximumPoint.Y, obj.GetSolid().MaximumPoint.Z);
        AABB box = new AABB(point1, point2);

        return box.IsInside(point);
    bool insideObject = InsideObject(pickedBeam, TestPoint);
    /// If TestPoint sits inside the panel, swap X and Y axis around
    if (insideObject)
        Vector tmp = new Vector(surfaceYaxis);
        surfaceYaxis = surfaceXaxis;
        surfaceXaxis = tmp;
        // Remake the coordinate system.
        surfaceCoordSystem = new CoordinateSystem(surfacePoint1, surfaceXaxis, surfaceYaxis);
    /// Off to actual insertion
    InsertGroove(model, surfaceXaxis, surfaceYaxis, surfaceCoordSystem);

Inserting the groove

We got required coordinate system and are ready to insert the groove:

        private void InsertGroove(Model model, Vector surfaceXaxis, Vector surfaceYaxis, CoordinateSystem surfaceCoordSystem)
            /// variables set by the user via "magic" data structure.
            double groovePosition = cPosition;       //input parameter1 : distance from panel surface
            double grooveLenght = cLenght;         //input parameter2  : groove height

            /// Prepare groove, profile is determined by the profile string.
            var Groove = new Beam(Beam.BeamTypeEnum.PANEL);
            Groove.Name = "GROOVE";
            Groove.Material.MaterialString = "Zero_weight";// "Anti material";
            Groove.Profile.ProfileString = "REC_D20*50-70";

            double startX = 0; double startY = 0; double startZ = 0;
            double endX = 0; double endY = 0; double endZ = 0;

            /// Place groove parallel to the long side of the panel's surface
            if (surfaceXaxis.GetLength() > surfaceYaxis.GetLength())
                startX = 0; startY = groovePosition;
                endX = grooveLenght; endY = groovePosition;
                startX = groovePosition; startY = 0;
                endX = groovePosition;  endY = grooveLenght;

            /// Very important - to keep grooves orientation in space proper switch to local coordinate system we calculated
            model.GetWorkPlaneHandler().SetCurrentTransformationPlane(new TransformationPlane(surfaceCoordSystem));

            /// Set start and end points for the groove
            Groove.StartPoint = new Point(startX, startY, startZ);
            Groove.EndPoint = new Point(endX, endY, endZ);

            /// Set grooves orientation. Sometimes this can be tricky depending on the profile. And might require extra measures.
            Position.PlaneEnum positionPlane = Position.PlaneEnum.MIDDLE;
            Position.RotationEnum positionRotation = Position.RotationEnum.TOP;
            Position.DepthEnum positionDepth = Position.DepthEnum.BEHIND;

            Groove.Position.Plane = positionPlane;
            Groove.Position.Rotation = positionRotation;
            Groove.Position.Depth = positionDepth;
            Groove.Position.RotationOffset = 0;
            /// Set transformation plane back to default global
            model.GetWorkPlaneHandler().SetCurrentTransformationPlane(new TransformationPlane());


In a similar fashion we calculate groove pocket positions and insert them as well. Main trick here is setting the transformation plane, otherwise you won't be able to properly position the groove.