////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        //
        //
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        public FeatureReport SendToParts(AssemblyDocument ParentDocument,
                                         List <ComponentOccurrence> Participants,
                                         PartFeature AsmFeature)
        {
            RectangularPatternFeature patternFeature = AsmFeature as RectangularPatternFeature;

            switch (_PatternMigrationMode)
            {
            case PatternMigrationModeEnum.kSendToPartsAsCollectionMode:
                return(SendToPartsAsCollection(ParentDocument, Participants, patternFeature));

            case PatternMigrationModeEnum.kSendToPartsAsPatternMode:
                return(SendToPartsAsPattern(ParentDocument, Participants, patternFeature));

            default:
                return(null);
            }
        }
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        //
        //
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        private static RectangularPatternFeature[] CopyRectangularPatternFeature(PartComponentDefinition partCompDef,
                                                                                 RectangularPatternFeature AsmFeature,
                                                                                 Matrix invTransfo,
                                                                                 int elementIdx,
                                                                                 ComponentOccurrence occurrence,
                                                                                 out ReportData[] reports)
        {
            List <RectangularPatternFeature> newFeatures = new List <RectangularPatternFeature>();

            ObjectCollection ParentFeatures = FeatureUtilities.CopyParentFeatures(partCompDef,
                                                                                  AsmFeature.Parent.Document as Document,
                                                                                  AsmFeature.ParentFeatures,
                                                                                  invTransfo,
                                                                                  occurrence,
                                                                                  out reports);

            if (ParentFeatures.Count == 0)
            {
                return(null);
            }

            Sketch3D sketch3D = partCompDef.Sketches3D.Add();

            List <RectangularPatternFeatureData> FeaturesDataList = new List <RectangularPatternFeatureData>();

            //Only along X Axis
            if (AsmFeature.XDirectionEntity != null && AsmFeature.YDirectionEntity == null)
            {
                UnitVector xDirection = FeatureUtilities.GetDirection(AsmFeature.XDirectionEntity);
                xDirection.TransformBy(invTransfo);

                Point         xDirStartPoint   = null;
                WorkAxis      xDirAxis         = null;
                SketchPoint3D xDirStartPoint3D = null;

                try
                {
                    xDirStartPoint = FeatureUtilities.GetPoint(AsmFeature.XDirectionStartPoint);
                    xDirStartPoint.TransformBy(invTransfo);

                    xDirAxis = partCompDef.WorkAxes.AddFixed(xDirStartPoint, xDirection, FeatureUtilities.ConstructionWorkAxis);

                    xDirStartPoint3D = sketch3D.SketchPoints3D.Add(xDirStartPoint, false);
                }
                catch
                {
                    xDirAxis = partCompDef.WorkAxes.AddFixed(partCompDef.WorkPoints[1].Point, xDirection, FeatureUtilities.ConstructionWorkAxis);
                }

                double count1 = (double)(AsmFeature.XCount.Value) - elementIdx + 1;

                //Check it's not the last pattern element
                if (count1 != 0 && elementIdx != (double)(AsmFeature.XCount.Value))
                {
                    RectangularPatternFeatureData featureData = newRectangularPatternFeatureData();

                    featureData.xCount = count1;

                    featureData.xSpacing = AsmFeature.XSpacing.Value;

                    featureData.naturalXDirection = AsmFeature.NaturalXDirection;

                    featureData.xDirectionSpacingType = AsmFeature.XDirectionSpacingType;

                    featureData.xDirAxis = xDirAxis;

                    featureData.xDirStartPoint3D = xDirStartPoint3D;

                    FeaturesDataList.Add(featureData);
                }

                double count2 = elementIdx;

                //Check it's not the first pattern element
                if (count2 != 0 && elementIdx != 1)
                {
                    RectangularPatternFeatureData featureData = newRectangularPatternFeatureData();

                    featureData.xCount = count2;

                    featureData.xSpacing = AsmFeature.XSpacing.Value;

                    featureData.naturalXDirection = !AsmFeature.NaturalXDirection;

                    featureData.xDirectionSpacingType = AsmFeature.XDirectionSpacingType;

                    featureData.xDirAxis = xDirAxis;

                    featureData.xDirStartPoint3D = xDirStartPoint3D;

                    FeaturesDataList.Add(featureData);
                }
            }

            //Only along Y Axis
            if (AsmFeature.YDirectionEntity != null && AsmFeature.XDirectionEntity == null)
            {
            }

            //Only along both Axes
            if (AsmFeature.XDirectionEntity != null && AsmFeature.YDirectionEntity != null)
            {
            }

            foreach (RectangularPatternFeatureData featureData in FeaturesDataList)
            {
                RectangularPatternFeature newFeature = partCompDef.Features.RectangularPatternFeatures.Add(ParentFeatures,

                                                                                                           featureData.xDirAxis,
                                                                                                           featureData.naturalXDirection,
                                                                                                           featureData.xCount,
                                                                                                           featureData.xSpacing,
                                                                                                           featureData.xDirectionSpacingType,
                                                                                                           featureData.xDirStartPoint3D,

                                                                                                           featureData.yDirAxis,
                                                                                                           featureData.naturalYDirection,
                                                                                                           featureData.yCount,
                                                                                                           featureData.ySpacing,
                                                                                                           featureData.yDirectionSpacingType,
                                                                                                           featureData.yDirStartPoint3D,

                                                                                                           AsmFeature.ComputeType,
                                                                                                           AsmFeature.OrientationMethod);

                foreach (FeaturePatternElement element in newFeature.PatternElements)
                {
                    if (newFeature.HealthStatus == HealthStatusEnum.kUpToDateHealth)
                    {
                        break;
                    }

                    if (element.Faces.Count == 0 && element.Index != 1)
                    {
                        element.Suppressed = true;
                    }
                }

                if (newFeature != null)
                {
                    newFeatures.Add(newFeature);
                }
            }

            return(newFeatures.ToArray());
        }
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        //
        //
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        public static FeatureReport SendToPartsAsCollection(AssemblyDocument ParentDocument,
                                                            List <ComponentOccurrence> Participants,
                                                            RectangularPatternFeature AsmFeature)
        {
            FeatureReport result = new FeatureReport(AsmFeature as PartFeature);

            if (FeatureUtilities.CreateBackupFile)
            {
                FeatureUtilities.BackupFile(Participants);
            }

            foreach (ComponentOccurrence occurrence in Participants)
            {
                if (!FeatureUtilities.IsValidOccurrence(occurrence))
                {
                    continue;
                }

                //Find if a pattern element affects this occurrence
                foreach (FeaturePatternElement element in AsmFeature.PatternElements)
                {
                    bool affected = false;

                    foreach (FaceProxy faceProxy in element.Faces)
                    {
                        if (faceProxy.ContainingOccurrence == occurrence)
                        {
                            affected = true;
                            break;
                        }
                    }

                    //first occurrence won't be migrated to parts.
                    //Need to migrate parent feature for that
                    if (!affected)
                    {
                        continue;
                    }

                    Matrix invTransfo = occurrence.Transformation;

                    Matrix patternElemTransfo = element.Transform;
                    patternElemTransfo.Invert();

                    invTransfo.TransformBy(patternElemTransfo);

                    invTransfo.Invert();

                    PartComponentDefinition partCompDef = occurrence.Definition as PartComponentDefinition;

                    ReportData[] reports = null;

                    ObjectCollection ParentFeatures = FeatureUtilities.CopyParentFeatures(partCompDef,
                                                                                          ParentDocument as Document,
                                                                                          AsmFeature.ParentFeatures,
                                                                                          invTransfo,
                                                                                          occurrence,
                                                                                          out reports);

                    foreach (ReportData reportData in reports)
                    {
                        //Place Feature Tag: associativity handling
                        //Not supported yet

                        /*FeatureAttributeManager.CreatePartFeatureTag(ParentDocument,
                         *  AsmFeature as PartFeature,
                         *  reportData.PartFeature,
                         *  occurrence);*/

                        result.addReportData(reportData);
                    }
                }
            }

            return(result);
        }
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        //
        //
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        public static FeatureReport SendToPartsAsPattern(AssemblyDocument ParentDocument,
                                                         List <ComponentOccurrence> Participants,
                                                         RectangularPatternFeature AsmFeature)
        {
            FeatureReport result = new FeatureReport(AsmFeature as PartFeature);

            if (FeatureUtilities.CreateBackupFile)
            {
                FeatureUtilities.BackupFile(Participants);
            }

            foreach (ComponentOccurrence occurrence in Participants)
            {
                if (!FeatureUtilities.IsValidOccurrence(occurrence))
                {
                    continue;
                }

                bool loop = true;

                int elementIdx = 1;

                Matrix patternElemTransfo = FeatureUtilities.Application.TransientGeometry.CreateMatrix();

                //Find first pattern element that affect this occurrence
                foreach (FeaturePatternElement element in AsmFeature.PatternElements)
                {
                    foreach (FaceProxy faceProxy in element.Faces)
                    {
                        if (faceProxy.ContainingOccurrence == occurrence)
                        {
                            patternElemTransfo = element.Transform;
                            patternElemTransfo.Invert();

                            elementIdx = element.Index;

                            loop = false;
                            break;
                        }
                    }

                    if (!loop)
                    {
                        break;
                    }
                }

                Matrix invTransfo = occurrence.Transformation;

                invTransfo.TransformBy(patternElemTransfo);

                invTransfo.Invert();


                PartComponentDefinition partCompDef = occurrence.Definition as PartComponentDefinition;

                ReportData[] reports = null;

                RectangularPatternFeature[] newFeatures = CopyRectangularPatternFeature(partCompDef,
                                                                                        AsmFeature,
                                                                                        invTransfo,
                                                                                        elementIdx,
                                                                                        occurrence,
                                                                                        out reports);

                foreach (ReportData reportData in reports)
                {
                    result.addReportData(reportData);
                }

                foreach (RectangularPatternFeature newFeature in newFeatures)
                {
                    result.addReportData(new ReportData(partCompDef.Document as PartDocument,
                                                        newFeature as PartFeature));
                }
            }


            return(result);
        }
예제 #5
0
        private void Button_ok_Click(object sender, EventArgs e)
        {
            // references to some useful objects
            TransientGeometry geom = m_inventorApplication.TransientGeometry;
            //PartDocument document = m_inventorApplication.ActiveDocument as PartDocument;
            //_Document document = m_inventorApplication.ActiveDocument;


            /*
             * if (document == null)
             * {
             *  AssemblyDocument asm = m_inventorApplication.ActiveDocument as AssemblyDocument;
             *  m_inventorApplication.ActiveDocument;
             * }
             */

            PartComponentDefinition def;   // = document.ComponentDefinition;
            UnitsOfMeasure          units; // = document.UnitsOfMeasure;
            UserParameters          user_params;
            Document document = m_inventorApplication.ActiveDocument;

            if (m_inventorApplication.ActiveDocument is AssemblyDocument)
            {
                AssemblyDocument doc  = m_inventorApplication.ActiveDocument as AssemblyDocument;
                PartDocument     part = doc.ActivatedObject as PartDocument;

                if (part == null)
                {
                    m_inventorApplication.ErrorManager.Show("Please activate a part!", true, false);
                    return;
                }

                def         = part.ComponentDefinition;
                units       = part.UnitsOfMeasure;
                user_params = part.ComponentDefinition.Parameters.UserParameters;
            }
            else if (m_inventorApplication.ActiveDocument is PartDocument)
            {
                PartDocument doc = m_inventorApplication.ActiveDocument as PartDocument;
                def         = doc.ComponentDefinition;
                units       = doc.UnitsOfMeasure;
                user_params = doc.ComponentDefinition.Parameters.UserParameters;
            }
            else
            {
                m_inventorApplication.ErrorManager.Show("Current document is neither an Assembly nor a Part.", true, false);
                return;
            }

            // get and check selected faces
            ObjectsEnumerator JustSelectedEntities = m_selects.SelectedEntities;

            if (JustSelectedEntities.Count == 0)
            {
                m_inventorApplication.ErrorManager.Show("Select at least one planar, rectangular face.", true, false);
                return;
            }

            foreach (Object _f in JustSelectedEntities)
            {
                Face f = _f as Face;
                if (f == null)
                {
                    m_inventorApplication.ErrorManager.Show("Somehow, you managed to select something that isn't a face. This should not happen, please report it.", true, false);
                    return;
                }

                if (f.Edges.Count != 4)
                {
                    m_inventorApplication.ErrorManager.Show("Please only select rectangular faces.", true, false);
                    return;
                }
            }

            // TODO: catch exception when invalid was is entered
            UserParameter tab_user_constr;

            if (m_dialog.mode_count.Checked)
            {
                tab_user_constr = user_params.AddByExpression("tab_count", m_dialog.tab_size_input.Text, UnitsTypeEnum.kUnitlessUnits);
            }
            else
            {
                tab_user_constr = user_params.AddByExpression("tab_size", m_dialog.tab_size_input.Text, UnitsTypeEnum.kDefaultDisplayLengthUnits);
            }



            int total_operations = JustSelectedEntities.Count;

            WorkAxis[]       extrusion_dir = new WorkAxis[total_operations];
            bool[]           long_edge_dir = new bool[total_operations];
            PlanarSketch[]   all_sketches  = new PlanarSketch[total_operations];
            Profile[]        profile       = new Profile[total_operations];
            ExtrudeFeature[] extrusion     = new ExtrudeFeature[total_operations];
            TwoPointDistanceDimConstraint[] tab_length_constr     = new TwoPointDistanceDimConstraint[total_operations];
            TwoPointDistanceDimConstraint[] tab_widthdepth_constr = new TwoPointDistanceDimConstraint[total_operations];
            TwoPointDistanceDimConstraint[] total_length_constr   = new TwoPointDistanceDimConstraint[total_operations];
            RectangularPatternFeature[]     rect_pattern          = new RectangularPatternFeature[total_operations];

            Transaction transaction = m_inventorApplication.TransactionManager.StartTransaction(m_inventorApplication.ActiveDocument, "LaserTab");

            // create extrusion feature for each face
            int i = 0;

            foreach (Object _f in JustSelectedEntities)
            {
                Face f = _f as Face;

                if (_f is FaceProxy)
                {
                    f = ((FaceProxy)_f).NativeObject;
                }

                // TODO: make sure active document is a partDocument and ActiveEditObject is not a sketch (should be also a partDocument?)
                // TODO: wrap it all into a ClientFeature
                // TODO: maybe also wrap it in a Transaction?



                // create sketch

                PlanarSketch sketch = def.Sketches.Add(f, false); // don't project anything
                //PlanarSketch sketch = def.Sketches.Add(f, true); // project existing geometry
                //PlanarSketch sketch = def.Sketches.AddWithOrientation(f, long_edge, true, true, long_edge.StartVertex, true);

                Edge   short_edge, long_edge;
                Vertex vert_origin, vert_short_end, vert_long_end;
                determine_orientation(f, out long_edge, out short_edge, out vert_origin, out vert_long_end, out vert_short_end);

                // remember wheter 'long_edge' starts or stops at 'P_orig' (which is important for the direction of the rectangular pattern)
                long_edge_dir[i] = long_edge.StartVertex == vert_origin;
                extrusion_dir[i] = def.WorkAxes.AddByLine(long_edge, true);

                // project important points
                SketchPoint P_orig  = sketch.AddByProjectingEntity(vert_origin) as SketchPoint;
                SketchPoint P_long  = sketch.AddByProjectingEntity(vert_long_end) as SketchPoint;
                SketchPoint P_short = sketch.AddByProjectingEntity(vert_short_end) as SketchPoint; // fails! :(


                // driven constraint of short dimension (determining thickness and depth of tab)
                tab_widthdepth_constr[i] = sketch.DimensionConstraints.AddTwoPointDistance(
                    P_orig, P_short, DimensionOrientationEnum.kAlignedDim,
                    P_short.Geometry, true);

                // driven constraint of long dimenstion (determining number/size of tabs)
                total_length_constr[i] = sketch.DimensionConstraints.AddTwoPointDistance(
                    P_orig, P_long, DimensionOrientationEnum.kAlignedDim,
                    P_long.Geometry, true);



                // appearantly, Profiles.AddForSolid() doesn't like lines that are made entirely out of projected points... (maybe because the line already exists?)
                SketchPoint P_orig_proj = P_orig, P_short_proj = P_short;

                P_short = sketch.SketchPoints.Add(P_short_proj.Geometry, false);
                P_orig  = sketch.SketchPoints.Add(P_orig_proj.Geometry, false);


                // create dimension constraints

                // TODO: calculate better position for text label



                //Debug.Print("constraint short: {0} = {1}", constr_short.Parameter.Expression, constr_short.Parameter.Value);
                //Debug.Print("constraint long: {0} = {1}", constr_long.Parameter.Expression, constr_long.Parameter.Value);

                // create endpoint for rectangle
                Point2d P_end2 = P_short.Geometry.Copy();
                Point2d P_end1 = P_orig.Geometry.Copy();

                Vector2d long_direction = P_orig.Geometry.VectorTo(P_long.Geometry);
                long_direction.ScaleBy(0.2);

                P_end1.TranslateBy(long_direction);
                P_end2.TranslateBy(long_direction);

                SketchPoint P_end1_sk = sketch.SketchPoints.Add(P_end1, false);
                SketchPoint P_end2_sk = sketch.SketchPoints.Add(P_end2, false);

                // constrain endpoints properly
                //sketch.GeometricConstraints.AddCoincident((SketchEntity)long_line2, (SketchEntity)P_end2_sk);
                //sketch.GeometricConstraints.AddCoincident((SketchEntity)long_line, (SketchEntity)P_end1_sk);

                // constraint for tab length (twice, once for each side of the rectangle)
                TwoPointDistanceDimConstraint tab_len_constraint1 = sketch.DimensionConstraints.AddTwoPointDistance(P_orig, P_end1_sk, DimensionOrientationEnum.kAlignedDim, P_end1);
                TwoPointDistanceDimConstraint tab_len_constraint2 = sketch.DimensionConstraints.AddTwoPointDistance(P_short, P_end2_sk, DimensionOrientationEnum.kAlignedDim, P_end2);
                tab_length_constr[i] = tab_len_constraint1;

                // {0}: total length
                // {1}: user input (count or length of single tab)

                string expr;
                if (m_dialog.mode_count.Checked)
                {
                    if (m_dialog.force_parity.Checked)
                    {
                        if (m_dialog.parity_even.Checked)
                        {
                            expr = "{0} / ( round({1}/2)*2 )";
                        }
                        else
                        {
                            expr = "{0} / ( round( ({1}+1)/2 )*2 - 1 )";
                        }
                    }
                    else
                    {
                        expr = "{0} / {1}";
                    }
                }
                else
                {
                    // TODO: take dropdown of >/</~ into account
                    if (m_dialog.force_parity.Checked)
                    {
                        if (m_dialog.parity_even.Checked)
                        {
                            expr = "{0} / ( round( {0}/{1}/2 )*2 )";
                        }
                        else
                        {
                            expr = "{0} / ( round( ({0}/{1}+1)/2 )*2 - 1)";
                        }
                    }
                    else
                    {
                        expr = "{0} / round({0}/{1})";
                    }
                }

                tab_len_constraint1.Parameter.Expression = string.Format(expr, total_length_constr[i].Parameter.Name, tab_user_constr.Name);
                tab_len_constraint2.Parameter.Expression = tab_len_constraint1.Parameter.Name;

                // create a rectangle based on these points
                // two-point rectangle is always axis-aligned -> doesn't work for rotated stuff
                //SketchEntitiesEnumerator rect = sketch.SketchLines.AddAsTwoPointRectangle(P_orig, P_end_sk);
                // this is cumbersome, as the third point is transient and therefore the rectangle would have to be constrained afterwards
                //SketchEntitiesEnumerator rect = sketch.SketchLines.AddAsThreePointRectangle(P_orig, P_short, P_end);


                ObjectCollection rect = m_inventorApplication.TransientObjects.CreateObjectCollection();
                rect.Add(sketch.SketchLines.AddByTwoPoints(P_orig, P_end1_sk));
                rect.Add(sketch.SketchLines.AddByTwoPoints(P_end1_sk, P_end2_sk));
                rect.Add(sketch.SketchLines.AddByTwoPoints(P_end2_sk, P_short));
                rect.Add(sketch.SketchLines.AddByTwoPoints(P_short, P_orig));

                // constrain rectangle

                if (m_dialog.offset.Checked)
                {
                    sketch.GeometricConstraints.AddCoincident((SketchEntity)rect[1], (SketchEntity)P_orig_proj);
                    sketch.GeometricConstraints.AddCoincident((SketchEntity)rect[3], (SketchEntity)P_short_proj);
                    sketch.GeometricConstraints.AddPerpendicular((SketchEntity)rect[1], (SketchEntity)rect[2]);

                    TwoPointDistanceDimConstraint offset_dist = sketch.DimensionConstraints.AddTwoPointDistance(P_orig, P_orig_proj, DimensionOrientationEnum.kAlignedDim, P_orig_proj.Geometry);
                    offset_dist.Parameter.Expression = tab_len_constraint1.Parameter.Name;
                }
                else
                {
                    sketch.GeometricConstraints.AddCoincident((SketchEntity)P_short, (SketchEntity)P_short_proj);
                    sketch.GeometricConstraints.AddCoincident((SketchEntity)P_orig, (SketchEntity)P_orig_proj);
                }

                sketch.GeometricConstraints.AddCoincident((SketchEntity)rect[1], (SketchEntity)P_long);
                sketch.GeometricConstraints.AddParallel((SketchEntity)rect[1], (SketchEntity)rect[3]);

                profile[i]      = sketch.Profiles.AddForSolid(false, rect);
                all_sketches[i] = sketch;

                i++;
            }

            // do extrusions
            for (i = 0; i < total_operations; i++)
            {
                string dist_expr;
                if (m_dialog.auto_depth.Checked)
                {
                    // use thickness of material
                    dist_expr = tab_widthdepth_constr[i].Parameter.Name;
                }
                else
                {
                    // use user input
                    // TODO: validate!
                    dist_expr = m_dialog.tab_depth_input.Text;
                }

                PartFeatureExtentDirectionEnum dir;
                PartFeatureOperationEnum       op;
                if (m_dialog.extrude_positive.Checked)
                {
                    dir = PartFeatureExtentDirectionEnum.kPositiveExtentDirection;
                    op  = PartFeatureOperationEnum.kJoinOperation;
                }
                else
                {
                    dir = PartFeatureExtentDirectionEnum.kNegativeExtentDirection;
                    op  = PartFeatureOperationEnum.kCutOperation;
                }

                // extrude said rectangle
                ExtrudeDefinition extrusion_def = def.Features.ExtrudeFeatures.CreateExtrudeDefinition(profile[i], op);
                extrusion_def.SetDistanceExtent(dist_expr, dir);
                extrusion[i] = def.Features.ExtrudeFeatures.Add(extrusion_def);
            }

            // do rectangular patterns
            for (i = 0; i < total_operations; i++)
            {
                // now repeat that extrusion
                ObjectCollection col = m_inventorApplication.TransientObjects.CreateObjectCollection();
                col.Add(extrusion[i]);

                // TODO: is ceil() actually correct here?
                string offset     = (m_dialog.offset.Checked ? "1" : "0");
                string count_expr = string.Format("ceil(round({0} / {1} - {2}) / 2)", total_length_constr[i].Parameter.Name, tab_length_constr[i].Parameter.Name, offset);


                RectangularPatternFeatureDefinition pattern_def =
                    def.Features.RectangularPatternFeatures.CreateDefinition(
                        col, extrusion_dir[i], long_edge_dir[i], count_expr, tab_length_constr[i].Parameter.Name + "*2");
                // TODO: we could use PatternSpacingType kFitToPathLength here...

                try
                {
                    rect_pattern[i] =
                        def.Features.RectangularPatternFeatures.AddByDefinition(pattern_def);
                }
                catch (Exception ex)
                {
                    Debug.Print("rect pattern failed: {0}", ex.Message);
                    Debug.Print("long edge: {0}, dir: {1}, count_expr = '{2}', len = '{3}'", extrusion_dir[i], long_edge_dir[i], count_expr, tab_length_constr[i].Parameter.Name + "*2");
                    transaction.End();
                    return;
                }
            }


            stop_selection();

            // create custom feature (called a ClientFeature by Inventor) containing all our sketches, extrusions and patterns in a single node

            object start_element;

            if (total_operations == 1)
            {
                // if there is only a single operation, the tree looks like this:

                /*
                 * - extrusion 1 (consumed sketch 1)
                 * - pattern 1
                 */
                start_element = extrusion[0];
            }
            else
            {
                // if there are multiple operations, the sketch is not consumed by the extrusion
                // and the tree looks like this:

                /*
                 * - sketch 1
                 * - sketch 2
                 * - extrusion 1
                 * - extrusion 2
                 * - pattern 1
                 * - pattern 2
                 */
                start_element = all_sketches[0];
            }

            ClientFeatureDefinition feature_def = def.Features.ClientFeatures.CreateDefinition("LaserTab", start_element, rect_pattern[total_operations - 1]);
            ClientFeature           feature     = def.Features.ClientFeatures.Add(feature_def, "{0defbf22-e302-4266-9bc9-fb80d5c8eb7e}");

            // load the icon for our custom feature if not done so already
            if (m_browser_icon == null)
            {
                stdole.IPictureDisp icon = PictureConverter.ImageToPictureDisp(Properties.Resources.browser_icon_16);
                m_browser_icon = document.BrowserPanes.ClientNodeResources.Add("{0defbf22-e302-4266-9bc9-fb80d5c8eb7e}", -1, icon);
            }

            NativeBrowserNodeDefinition ndef = document.BrowserPanes[1].GetBrowserNodeFromObject(feature).BrowserNodeDefinition as NativeBrowserNodeDefinition;

            ndef.OverrideIcon = m_browser_icon;

            transaction.End();
        }