Need Help with Edge Based Lofted Profile via API

The challenge...

I am trying to make a mixing element programmatically.  When complete it will look something like this:

Mixing Element.jpg

The trouble I am having is with the .AddLoftedProtrusion command.  I am getting a type mismatch error.  I believe that it is probably the crosssection argument (or it could be the Origins argument which is looking for Double - but I doubt that is the problem, I think I have it right) that is of the wrong type, but I as of yet haven't figured out what it actually is looking for.  I am working from CurvesByTables (which you can see in the attached part as well as the code below).  There are only 2 options for cross section (Profile or Edge based).  I originally tried passing the curvebytable objects to the lofted protrusion command - no joy.  I used the Spy tool which I learned of at the last SEU to figure out how to get the edges out of the curvesbytable objects.  Still no luck.  In the code below you may note that I am connecting to a PTC Mathcad Prime 3.1 worksheet to get some information (which was a very painful experience interfacing with since they charge $9k for the SDK - really, who would pay that for simple examples that should be included in proper documentation).  That worksheet also provides the Excel worksheets for the curves.  Unfortunately, I cannot share that with you.  Here is the code and a snapshot of Spy at a breakpoint just prior to attempting the loft command (which also corresponds with the attached part):


using System;
using System.Diagnostics;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using Ptc.MathcadPrime.Automation;
using SolidEdgeCommunity.Extensions;
using SolidEdgePart;

namespace MathcadPrimeC
public partial class Form1 : Form
Ptc.MathcadPrime.Automation.ApplicationCreatorClass MCappCreate = new ApplicationCreatorClass();
ApplicationCreator MCapp = null;

SolidEdgeFramework.Application SEapp = null;
SolidEdgeFramework.Documents SEdocuments = null;
SolidEdgePart.PartDocument SEpartDocument = null;

double[,] dblODcurves = new double[4, 3];
double[,] dblrootcurves = new double[4, 3];

public Form1()

private void button1_Click(object sender, EventArgs e)



public void Mathcad()
var MCapp = (ApplicationCreator)MCappCreate; // Open Mathcad
MCapp.Visible = true;

//Open Parameters Worksheet
IMathcadPrimeWorksheet3 wsParam = (IMathcadPrimeWorksheet3)MCapp.Open(@"S:\ENGINEER\mathcad\Secret\Basic Secret Parameters.mcdx");

//Get List of Machine Sizes and populate checkedlistBox
IMathcadPrimeOutputMatrixResultAs BoreResult = wsParam.OutputGetMatrixValueAs("Bore", "mm");
double wsBoreResultRowsCount = BoreResult.MatrixResult.Rows;
double dblBore;
List<string> BoreList = new List<string>();

for (int i = 0; i < wsBoreResultRowsCount; i++)
BoreResult.MatrixResult.GetMatrixElement(i, 0, out dblBore);
BindingSource BoreBindingSource = new BindingSource();
BoreBindingSource.DataSource = BoreList;
checkedListBore.DataSource = BoreBindingSource.DataSource;

//Get Matchcad output for D_od, D_root, Ax_pitch
IMathcadPrimeOutputMatrixResultAs DodResult = wsParam.OutputGetMatrixValueAs("D_od", "mm");
IMathcadPrimeOutputMatrixResultAs DrootResult = wsParam.OutputGetMatrixValueAs("D_root", "mm");
IMathcadPrimeOutputMatrixResultAs AxpitchResult = wsParam.OutputGetMatrixValueAs("Ax_pitch", "mm");

//Open OD Curves Worksheet
IMathcadPrimeWorksheet3 wsODcurves = (IMathcadPrimeWorksheet3)MCapp.Open(@"S:\ENGINEER\mathcad\Secret\Secret OD Flight Curves Tight Clearance 60.mcdx");
//Get Matchcad output for od_intersections
IMathcadPrimeOutputMatrixResult ODcurvesResult = wsODcurves.OutputGetMatrixValue("od_intersections");
//Open OD Curves Worksheet
IMathcadPrimeWorksheet3 wsRootcurves = (IMathcadPrimeWorksheet3)MCapp.Open(@"S:\ENGINEER\mathcad\Secret\Secret Root Flight Curves Tight Clearance 60.mcdx");
//Get Matchcad output for od_intersections
IMathcadPrimeOutputMatrixResult RootcurvesResult = wsRootcurves.OutputGetMatrixValue("root_intersections");

//Tight Clearance Root curves
for (int row = 0; row <= 3; row++)
for (int col = 0; col <= 2; col++)
double x;
RootcurvesResult.MatrixResult.GetMatrixElement(row, col, out x);
dblrootcurves[row, col] = x/1000; //divide by 1000 to change from mm to meters

//Tight Clearance OD curves
for (int row = 0; row <= 3; row++)
for (int col = 0; col <= 2; col++)
double x;
ODcurvesResult.MatrixResult.GetMatrixElement(row, col, out x);
dblODcurves[row, col] = x / 1000; //divide by 1000 to change from mm to meters

//IMathcadPrimeOutputs wsParamOutputs = wsParam.Outputs;

//for (int i = 0; i <= wsParamOutputs.Count - 1; i++)
// var wsAlias = wsParamOutputs.GetAliasByIndex(i);
// var wsResult = wsParam.OutputGetMatrixValueAs(wsAlias,"mm");

// double Output;
// wsResult.MatrixResult.GetMatrixElement(i, 0, out Output);


public void SolidEdge()
// Register with OLE to handle concurrency issues on the current thread.

// Connect to or start Solid Edge.
var SEapp = SolidEdgeCommunity.SolidEdgeUtils.Connect(true, true);

// Get a reference to the documents collection.
var SEdocuments = SEapp.Documents;

// Create a new part document using PROGID.
//partDocument = (SolidEdgePart.PartDocument)documents.Add("SolidEdge.PartDocument");

// Create a new part document using PROGID defined in Interop.SolidEdge.dll.
//partDocument = (SolidEdgePart.PartDocument)documents.Add(SolidEdgeSDK.PROGID.SolidEdge_PartDocument);

// Create a new part document using SolidEdge.Community.dll extension method.
var SEpartDocument = SEdocuments.AddPartDocument();

// Create a new part document using SolidEdge.Community.dll extension method.
//partDocument = documents.Add<SolidEdgePart.PartDocument>();

// Always a good idea to give SE a chance to breathe.

SEpartDocument.ModelingMode = ModelingModeConstants.seModelingModeSynchronous;

Constructions SEConstructions = null;

SEConstructions = SEpartDocument.Constructions;

var curvesbytables = SEConstructions.CurvesByTables;
curvesbytables.Add(@"S:\ENGINEER\mathcad\Secret\Flight Geometry\Root-Tight-60-Curve1.xlsx");
curvesbytables.Add(@"S:\ENGINEER\mathcad\Secret\Flight Geometry\Root-Tight-60-Curve2.xlsx");
curvesbytables.Add(@"S:\ENGINEER\mathcad\Secret\Flight Geometry\Root-Tight-60-Curve3.xlsx");
curvesbytables.Add(@"S:\ENGINEER\mathcad\Secret\Flight Geometry\Root-Tight-60-Curve4.xlsx");
curvesbytables.Add(@"S:\ENGINEER\mathcad\Secret\Flight Geometry\OD-Tight-60-Curve1.xlsx");
curvesbytables.Add(@"S:\ENGINEER\mathcad\Secret\Flight Geometry\OD-Tight-60-Curve2.xlsx");
//Next 2 used for tight clearance only
curvesbytables.Add(@"S:\ENGINEER\mathcad\Secret\Flight Geometry\OD-Tight-60-Curve3.xlsx");
curvesbytables.Add(@"S:\ENGINEER\mathcad\Secret\Flight Geometry\OD-Tight-60-Curve4.xlsx");
curvesbytables.Add(@"S:\ENGINEER\mathcad\Secret\Flight Geometry\Guide-Tight-60-Curve1.xlsx");
curvesbytables.Add(@"S:\ENGINEER\mathcad\Secret\Flight Geometry\Guide-Tight-60-Curve2.xlsx");
curvesbytables.Add(@"S:\ENGINEER\mathcad\Secret\Flight Geometry\Guide-Tight-60-Curve3.xlsx");
curvesbytables.Add(@"S:\ENGINEER\mathcad\Secret\Flight Geometry\Guide-Tight-60-Curve4.xlsx");

object[] edgecollection = new object[12];

for (int i = 1; i <= 12; i++)
ConstructionModel model = SEConstructions.Item(i);
SolidEdgeGeometry.Body body = (SolidEdgeGeometry.Body)model.Body;
SolidEdgeGeometry.Vertices verts = (SolidEdgeGeometry.Vertices)body.Vertices;
SolidEdgeGeometry.Vertex vert = (SolidEdgeGeometry.Vertex)verts.Item(1);
SolidEdgeGeometry.Edges edges = (SolidEdgeGeometry.Edges)vert.Edges;
SolidEdgeGeometry.Edge edge = (SolidEdgeGeometry.Edge)edges.Item(1);
edgecollection[i-1] = edge;

object[] rootloop = { edgecollection[0], edgecollection[1], edgecollection[2], edgecollection[3] };
object[] odloop = { edgecollection[4], edgecollection[5], edgecollection[6], edgecollection[7] };
object[] crosssections = { rootloop, odloop };
object[] guideedges = { edgecollection[9], edgecollection[9], edgecollection[10], edgecollection[11] };

object[,] intersections = new object[2, 3] { { dblrootcurves[0, 0], dblrootcurves[0, 1], dblrootcurves[0, 2] }, { dblODcurves[0, 0], dblODcurves[0, 1], dblODcurves[0, 2] } };

FeaturePropertyConstants[] crosssectiontype = { FeaturePropertyConstants.igEdgeBasedCrossSection, FeaturePropertyConstants.igEdgeBasedCrossSection };
Model SEmodel = null;
SEmodel = SEpartDocument.Models.AddLoftedProtrusion(NumSections: 2, CrossSections: crosssections, CrossSectionTypes: crosssectiontype,
Origins: intersections, SegmentMaps: 0, MaterialSide: FeaturePropertyConstants.igLeft, StartExtentType: FeaturePropertyConstants.igLeft,
StartExtentDistance: 0, StartSurfaceOrRefPlane: null, EndExtentType: FeaturePropertyConstants.igNone, EndExtentDistance: 0,
EndSurfaceOrRefPlane: null, StartTangentType: FeaturePropertyConstants.igNone, StartTangentMagnitude: 0,
EndTangentType: FeaturePropertyConstants.igNone, EndTangentMagnitude: 0); //, NumGuideCurves: 4, GuideCurves: guideedges);

catch (System.Exception ex)





Re: Need Help with Edge Based Lofted Profile via API

First of all let me say, that I never created a LoftedProtrusion programmatically and I also tried to create one based on your part document, but without success.

I have created LoftedSurfaces successfully in the past and would like to share some information on that which might help:

1) Like you already know, there are two types of LoftedProtrusions/-Surfaces: ProfileBased and EdgeBased

2) The way how you prepare the arguments for the AddLoftedProtrusion method depends on the type of Protrusion (see the following table):


                                              ProfileBased                           EdgeBased

CrossSections (Array of):       Profile                                     Edge / Edges

SectionTypes (Array of):        igProfileBasedCrossSection    igEdgeBasedCrossSection

Origins (Array of):                  Double(2)                              Vertex


As you are creating a protrusion with a closed loop of edges, you will need to create an Edges collection from your set of input edges first. In case of a circular edge, you would use a single circular Edge as the cross section!!


Dim objCollection As Edges = objBody.CreateCollection(seEdgeCollection)

You may use the body of the first edge here.


3) Use the StartVertex of your first edge as the Origin of each cross section


I hope this helps you to get the protrusion done. The best I could get during my tests was a failed Protrusion feature (in Ordered Mode, Sync gave me more problems) saying that there was an invalid cross section defined.

Re: Need Help with Edge Based Lofted Profile via API

Thank you Martin,

It was good to know that it was looking for Vertex instead of Double for Origin.  I still was not able to get it working.  I ended up switching direction since it is so difficult to troubleshoot a command with so many arguments.  I am now trying (and failing) to use surfaces and stitch them together to get the solid.  I started by adding this code to the code I previously posted:


SurfaceByBoundaries seSurfByBoundaries = null;
SurfaceByBoundary surface_1 = null;
SurfaceByBoundary surface_2 = null;

seSurfByBoundaries = SEConstructions.SurfaceByBoundaries;
surface_1 = seSurfByBoundaries.Add(4, rootloop);
surface_2 = seSurfByBoundaries.Add(4, odloop);


I get the following error when it gets to the second surface:

Unspecified error (Exception from HRESULT: 0x80004005 (E_FAIL))


If I comment out surface_1 it makes surface_2 just fine.

Spy shows this:

surface trouble.jpg

I am not sure why it shows different parents for the 2 surfaces (1 is ConstructionModel and 1 is PartDocument).


Any hints would be great!

Re: Need Help with Edge Based Lofted Profile via API

I moved to Ordered instead of Synchronous and it worked with no error...Hmmm