TheSwamp

Code Red => .NET => Topic started by: bikelink on June 02, 2010, 02:02:11 AM

Title: how to move a face in a solid ?
Post by: bikelink on June 02, 2010, 02:02:11 AM
I'm trying to move a face in a solid3d  via code . You can consider a simple box. I'm not able to understand how can I obtain the SubEntityID[]
needed to perform the methods without selection


SubentityId[] subEntsID = ????
mySolid.ExtrudeFaces(subEntsID, 100, 0);

s.TransformFaces(????,ecc..)


thanks in advance for any idea!
Title: Re: how to move a face in a solid ?
Post by: Kerry on June 02, 2010, 02:09:52 AM

Quote
... perform the methods without selection


What does your program know about the solid ??

how will the program know which face to move ??

what have you tried ??

are you familiar with   acdbmgdbrep.dll ??
 
Title: Re: how to move a face in a solid ?
Post by: bikelink on June 02, 2010, 02:16:36 AM
ok...


Code: [Select]
using (Transaction t = d.TransactionManager.StartTransaction())
                {
                    Solid3d s = t.GetObject(IdSolid3d, OpenMode.ForWrite) as Solid3d;
                   
                   
                   
                    //// found the face...
                    using (AcBr.Brep brp = new AcBr.Brep(s))
                    {
                        ObjectId[] ids = new ObjectId[1];
                        ids[0] = s.ObjectId;
                        SubentityId index = new SubentityId(SubentityType.Null, 0);
                        FullSubentityPath path = new FullSubentityPath(ids, index);
                        s.Highlight(path, true);


                        Extents3d e =  s.GeometricExtents;
                        AcBr.BrepFaceCollection facce = brp.Faces;
                        IList<AcBr.Face> faccedaspostare = new List<AcBr.Face>(0);
                        foreach (AcBr.Face [color=blue]f[/color] in facce)
                        {
                            IList<Point3d> ptfaccia = new List<Point3d>(0);
                            AcBr.FaceLoopCollection fl =  f.Loops;
                            foreach (AcBr.BoundaryLoop l in fl)
                            {

 so I Have a acbrface...and I Know the face that I want to move.
How can I get th subentityID about a face (or more faces) ?
And other question is ... Why the property "f.SubentityPath" just to see the value throws an exception ?

Title: Re: how to move a face in a solid ?
Post by: Kerry on June 02, 2010, 03:47:20 AM

can you post all the code and the drawing .. I'll try to have a look early tomorrow .. won't have much time , sorry.

Is the face you want planar ??
Title: Re: how to move a face in a solid ?
Post by: bikelink on June 02, 2010, 04:01:24 AM
Code: [Select]
static public void MoveTopFacesOfSolid(Document d, ObjectId IdSolid3d, double hfinale,Vector3d vetextr)
        {
            using (DocumentLock lk = d.LockDocument())
            {
                using (Transaction t = d.TransactionManager.StartTransaction())
                {
                    Solid3d s = t.GetObject(IdSolid3d, OpenMode.ForWrite) as Solid3d;
                    
                    
                    
                    //// found the face...
                    using (AcBr.Brep brp = new AcBr.Brep(s))
                    {
                        ObjectId[] ids = new ObjectId[1];
                        ids[0] = s.ObjectId;
                        SubentityId index = new SubentityId(SubentityType.Null, 0);
                        FullSubentityPath path = new FullSubentityPath(ids, index);
                        s.Highlight(path, true);


                        Extents3d e =  s.GeometricExtents;
                        AcBr.BrepFaceCollection facce = brp.Faces;
                        IList<AcBr.Face> faccedaspostare = new List<AcBr.Face>(0);
                        foreach (AcBr.Face f in facce)
                        {
                            IList<Point3d> ptfaccia = new List<Point3d>(0);
                            AcBr.FaceLoopCollection fl =  f.Loops;
                            foreach (AcBr.BoundaryLoop l in fl)
                            {
                                // se un cerchio
                                if (l.LoopType == Autodesk.AutoCAD.BoundaryRepresentation.LoopType.LoopWinding)
                                {
                                    foreach (AcBr.Edge filo in l.Edges)
                                    {
                                        Curve3d c = filo.Curve;

                                        Plane planz;
                                        bool b = c.IsPlanar(out planz);
                                        if (planz.Normal == vetextr)
                                        {
                                            faccedaspostare.Add(f);
                                        }
                                        planz.Dispose();
                                    }

                                }
                                else
                                {
                                    foreach (AcBr.Vertex v in l.Vertices)
                                    {
                                        ptfaccia.Add(v.Point);
                                    }
                                    // se la faccia è normale alla direzione...e siamo sopra.
                                    Vector3d vf = ptfaccia[0] - ptfaccia[1];
                                    Vector3d vfperp = vf.GetPerpendicularVector();
                                    Vector3d vfnorm = vf.CrossProduct(vfperp);
                                    if (vfnorm.IsParallelTo(vetextr) && ptfaccia[0].Z == e.MaxPoint.Z)
                                        faccedaspostare.Add(f);

                                }
                            }
                            
                              
                                //DoubleCollection Rad = new  DoubleCollection();
                                //Rad.Add(2.0);
                                //DoubleCollection startsetback = new DoubleCollection();
                                //startsetback.Add(0);
                                //DoubleCollection endsetback = new DoubleCollection();
                                //endsetback.Add(0);

                                //SubentityId[] sub1 = new SubentityId[] { new SubentityId(SubentityType.Edge, 0) };
                                //s.FilletEdges(sub1, Rad, startsetback,endsetback);

                                //SubentityId si = new SubentityId(SubentityType.Face, 0);
                            SubentityId[] sub2 = new SubentityId[] {  new SubentityId(SubentityType.Face, 0)};
                            
                                                                    
                            s.ExtrudeFaces(sub2, 100, 0);
                              
                                
                            }
                        }

                    t.Commit();
                }
            }
        
        }



[CommandMethod("mvts")]
         public void mvts()
         {
         Document d = (Document)Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument;
         Editor e = d.Editor;
             PromptEntityOptions o = new PromptEntityOptions("select a solid");
             o.SetRejectMessage("is not a  solid");
             PromptEntityResult r =  e.GetEntity(o);
             if (r.Status == PromptStatus.OK)
             {
                 MoveTopFacesOfSolid(d, r.ObjectId, 1000, Vector3d.ZAxis);
            
             }
         }

that's all..
The top faces  must be moved and i don't understand how can I take the entities :( without user selection
thanks for Your help. Any idea is always better of mine..now.
Title: Re: how to move a face in a solid ?
Post by: MickD on June 02, 2010, 05:14:19 PM
without studying your code too much, if you can get the face, dig deeper to get each vertice (using the traversal classes) of the face from it's edges and transform the vertices and the edges and face should follow.
hth
Title: Re: how to move a face in a solid ?
Post by: bikelink on June 02, 2010, 11:46:24 PM
no. it's not right.
1) You can't apply a transform at acbr vertex.
2) If You apply a transform at Point (property of vertex) nothing happens

 :realmad:


some words in Autodesk discussion groups sounds bad..like

There's little API support for solid modeling in AutoCAD
because Autodesk doesn't want them to be programmable.

http://discussion.autodesk.com/forums/thread.jspa?messageID=6356439&#6356439 (http://discussion.autodesk.com/forums/thread.jspa?messageID=6356439&#6356439)

I don't want to believe it and I'm sure that a solid could be modified in a dimension.. isn't it?
Title: Re: how to move a face in a solid ?
Post by: SEANT on June 03, 2010, 04:21:37 AM
I ran into this same issue when writing the routine in this post:
http://www.theswamp.org/index.php?topic=30499.0

At the time I assumed it was because the SubentID wasn’t created until it could be associated with the appropriate graphical elements. 

Now, looking at your situation where there shouldn’t be any problem getting Face.SubentityPath, it does seem a bit suspicious.

There may be more avenues to explore, I’ll post back if I find anything.
Title: Re: how to move a face in a solid ?
Post by: SEANT on June 04, 2010, 12:09:33 PM
A couple of irritating quirks have come to light here:

It would appear that there is some initialization going on that does not occur with the Editor.GetEntity, hence the problem with “f.SubentityPath”.  The initialization is available with Solid3d.GetSubentityPathsAtGraphicsMarker, however, so it is possible to get a valid SubentID from the backside.

The other quirk is that the SubentityId[] requirement for all of the SolidEditing “Faces” methods is not implemented as the documentation states.  I can’t find an object/array that is compatible.  The SolidEditing “Face” methods do seem to work, and is what I used as a workaround in the sample.

Code: [Select]
        [CommandMethod("mvts")]
        public void mvts()
        {
            Document d = (Document)Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument;
            Editor e = d.Editor;


            PromptEntityOptions o = new PromptEntityOptions("select a solid");
            o.SetRejectMessage("is not a  solid");
            o.AddAllowedClass(typeof(Solid3d), true);
            PromptEntityResult r = e.GetEntity(o);
            if (r.Status == PromptStatus.OK)
            {
                MoveTopFacesOfSolid2(d, r.ObjectId, 1, Vector3d.ZAxis);

            }
        }


        static public void MoveTopFacesOfSolid2(Document d, ObjectId IdSolid3d, double hfinale, Vector3d vetextr)
        {

            using (Transaction t = d.TransactionManager.StartTransaction())
            {
               
                Solid3d s = t.GetObject(IdSolid3d, OpenMode.ForRead) as Solid3d;
                AcBr.Brep brp = new AcBr.Brep(s);
                Point3d pt = new Point3d();
                Matrix3d mtrx = new Matrix3d();
                int numIns = new int();
                ObjectId[] ids = null;
                FullSubentityPath[] fsP;
                for (int i = 1; i < 1000; i++)
                {
                    try
                    {
                        fsP = s.GetSubentityPathsAtGraphicsMarker
                            (SubentityType.Face, i, pt, mtrx, numIns, ids); //If GSM points to face
                        Autodesk.AutoCAD.BoundaryRepresentation.Face brepFace = new Autodesk.AutoCAD.BoundaryRepresentation.Face(fsP[0]);
                        if (TestFace(brepFace, vetextr))
                        {

                            s.UpgradeOpen();
                            SubentityId sid = fsP[0].SubentId;                                                                                                               
                            Region reg = s.CopyFace(sid) as Region;
                            Solid3d s2 = new Solid3d();
                                                                                                                                                                                                                                                                                                                  s2.Extrude(reg, 1, 0);
                            s2.SetDatabaseDefaults();
                            Database m_db = HostApplicationServices.WorkingDatabase;
                            BlockTableRecord btr = (BlockTableRecord)(t.GetObject(m_db.CurrentSpaceId, OpenMode.ForWrite));
                            btr.AppendEntity(s2);
                            t.AddNewlyCreatedDBObject(s2, true);


                            s.BooleanOperation(BooleanOperationType.BoolUnite, s2);
                            break;
                        }

                    }
                    catch //If GSM points to non-face
                    {
                        continue;
                    }

                }
                t.Commit();
            }
        }

        static public bool TestFace(AcBr.Face f, Vector3d vetextr)
        {
            ExternalBoundedSurface ebs = f.Surface as ExternalBoundedSurface;
            if (ebs.IsPlane)
            {
                Plane BP = ebs.BaseSurface as Plane;
                return BP.Normal == vetextr;

            }

            return false;
        }

Title: Re: how to move a face in a solid ?
Post by: bikelink on June 07, 2010, 08:29:48 AM
ok...today i'll try as soon as possible Your solution. Thank You very much for your attention and code.
Title: Re: how to move a face in a solid ?
Post by: bikelink on June 14, 2010, 06:02:43 AM
ok...today i'll try as soon as possible Your solution. Thank You very much for your attention and code.

that's Works!!!
Thank You very much SEANT! I haven't idea to loop around with
fsP = s.GetSubentityPathsAtGraphicsMarker
                            (SubentityType.Face, i, pt, mtrx, numIns, ids); //If GSM points to face
Autodesk.AutoCAD.BoundaryRepresentation.Face brepFace = new Autodesk.AutoCAD.BoundaryRepresentation.Face(fsP[0]);


and try/catch to iterate..
But for me is a  mistery the method exposed in api for brep.."extrudefaces"
Title: Re: how to move a face in a solid ?
Post by: SEANT on June 15, 2010, 03:31:49 AM
But for me is a  mistery the method exposed in api for brep.."extrudefaces"

I agree.  That situation is rather irritating. 
Perhaps one of our C++ proficient brethren could test that in native ARX to see if and how those particular methods work.  :angel:
Title: Re: how to move a face in a solid ?
Post by: ahlzl on June 15, 2010, 08:17:10 AM
sorry,I do not speak English。

I can extrudeFace by ARX,but Wrapper to C#, fail! :oops:

Code: [Select]
static void ahlzlArxProject1_MyCommand1(void)
{
// Add your code for command ahlzlArxProject1._MyCommand1 here
AcDbObjectId entId = NULL;
AcDbEntity *pEnt;
AcDb3dSolid *pSolid3d;
AcArray<AcDbSubentId*> subIdArr;
AcEdSolidSubentitySelector subSel;

if (subSel.selectFaces(entId, subIdArr) == Acad::eOk)
{
if (acdbOpenAcDbEntity(pEnt, entId, AcDb::kForWrite) == Acad::eOk)
{
pSolid3d = (AcDb3dSolid*)pEnt;
for (int i = 0; i < subIdArr.length(); i++)
{
AcDbSubentId subId(AcDb::kFaceSubentType, subIdArr[i]->index());
pSolid3d->extrudeFaces(subIdArr, 100.0, 0.0);
}
pSolid3d->close();
}
}
}
Title: Re: how to move a face in a solid ?
Post by: SEANT on June 20, 2010, 06:10:21 AM
ahlzl,

Thanks for testing that for me. 

It is a shame that the .NET wrapper is not working as expected.   :-(

I suppose this is another indication that stepping up to native ARX is a worthwhile endeavor.
Title: Re: how to move a face in a solid ?
Post by: It's Alive! on June 20, 2010, 07:05:17 AM
I suppose this is another indication that stepping up to native ARX is a worthwhile endeavor.

Jump in, the water is fine  8-)
Title: Re: how to move a face in a solid ?
Post by: ahlzl on June 20, 2010, 10:47:00 AM
Wrapper to C#, Success!(CAD2008 + VS2005)

step1: Create a new ARX project

step2: Add a class: MyClass

step3: In MyClass.h, Add code:
Code: [Select]
#include "tchar.h"
step4: In MyClass.cpp, Add code:
Code: [Select]
extern "C" __declspec(dllexport) AcDb3dSolid* GetSubentIndexs()
{
AcDbObjectId entId = NULL;
AcArray<AcDbSubentId*> subIdArr;
AcEdSolidSubentitySelector subSel;
AcDb3dSolid *pSolid3d = NULL;

if (subSel.selectFaces(entId, subIdArr) == Acad::eOk)
{
acdbOpenObject(pSolid3d, entId, AcDb::kForWrite);

TCHAR subIdIndexStrs[1000];
TCHAR indexStr[10];
int index = 0;

for (int i = 0; i < subIdArr.length(); i++)
{
index = (int)(subIdArr[i]->index());
_itow(index, indexStr, 10);

if (i == 0)
{
wcscpy(subIdIndexStrs, indexStr);
}
else
{
wcscat(subIdIndexStrs, _T("-"));
wcscat(subIdIndexStrs, indexStr);
}
}

struct resbuf rb;
rb.restype = RTSTR;
rb.resval.rstring = subIdIndexStrs;
acedSetVar(_T("users1"), &rb);
return pSolid3d;
}
return NULL;
}

step5: Compile, Generate *.arx file.

step6: copy the file to AutoCAD Support Directory

strp7: Create a new C# project, Add code
Code: [Select]
using System;
using System.Runtime.InteropServices;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.Colors;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.Runtime;

namespace Cs
{
    public class Class1
    {
        [DllImport("yourARX.arx", CallingConvention = CallingConvention.Cdecl, EntryPoint = "GetSubentIndexs")]
        public static extern IntPtr GetSubentIndexs();

        [CommandMethod("test")]
        public void MyTest()
        {
            Color myColor = Color.FromColorIndex(ColorMethod.ByLayer, 1);
 
            IntPtr rb = GetSubentIndexs();
            if (rb != IntPtr.Zero)
            {
                Solid3d solid3dEnt = DisposableWrapper.Create(typeof(Solid3d), rb, true) as Solid3d;

                if (solid3dEnt != null)
                {
                    string users1Value = (string)Application.GetSystemVariable("users1");
                    String[] strs = users1Value.Split(new char[] { '-' });
                    for (int i = 0; i < strs.Length; i++)
                    {
                        SubentityId subId = new SubentityId(SubentityType.Face, Convert.ToInt16(strs[i]));
                        solid3dEnt.SetSubentityColor(subId, myColor);
                    }
                    solid3dEnt.Close();
                }
            }
        }
    }
}
Title: Re: how to move a face in a solid ?
Post by: It's Alive! on June 20, 2010, 07:28:47 PM
Good start! a couple of small issues, first,  in the ARX you have a memory leak at subIdArr, you need to iterate the array and delete the pointers, Second you pass an object that's opened for write,  when you should probably pass an ObjectID  :-)
Title: Re: how to move a face in a solid ?
Post by: ahlzl on June 20, 2010, 09:20:39 PM
Very grateful to the guidance of Daniel!

First Problem,Is this it?

Code: [Select]
static void ahlzlArxProject1_MyCommand1(void)
{
// Add your code for command ahlzlArxProject1._MyCommand1 here
AcDbObjectId entId = NULL;
AcDbEntity *pEnt;
AcDb3dSolid *pSolid3d;
AcArray<AcDbSubentId*> subIdArr;
AcEdSolidSubentitySelector subSel;

if (subSel.selectFaces(entId, subIdArr) == Acad::eOk)
{
if (acdbOpenAcDbEntity(pEnt, entId, AcDb::kForWrite) == Acad::eOk)
{
pSolid3d = (AcDb3dSolid*)pEnt;
for (int i = 0; i < subIdArr.length(); i++)
{
AcDbSubentId subId(AcDb::kFaceSubentType, subIdArr[i]->index());
pSolid3d->extrudeFaces(subIdArr, 100.0, 0.0);
[color=red]delete subIdArr[i][/color];
}
pSolid3d->close();
}
}
}

Code: [Select]
extern "C" __declspec(dllexport) AcDb3dSolid* GetSubentIndexs()
{
AcDbObjectId entId = NULL;
AcArray<AcDbSubentId*> subIdArr;
AcEdSolidSubentitySelector subSel;
AcDb3dSolid *pSolid3d = NULL;

if (subSel.selectFaces(entId, subIdArr) == Acad::eOk)
{
acdbOpenObject(pSolid3d, entId, AcDb::kForWrite);

TCHAR subIdIndexStrs[1000];
TCHAR indexStr[10];
int index = 0;

for (int i = 0; i < subIdArr.length(); i++)
{
index = (int)(subIdArr[i]->index());
_itow(index, indexStr, 10);

if (i == 0)
{
wcscpy(subIdIndexStrs, indexStr);
}
else
{
wcscat(subIdIndexStrs, _T("-"));
wcscat(subIdIndexStrs, indexStr);
}
[color=red]delete subIdArr[i];[/color]
}

struct resbuf rb;
rb.restype = RTSTR;
rb.resval.rstring = subIdIndexStrs;
acedSetVar(_T("users1"), &rb);
return pSolid3d;
}
return NULL;
}
Title: Re: how to move a face in a solid ?
Post by: It's Alive! on June 20, 2010, 10:24:29 PM
I might do something like this (untested) see attachment

.h
Code: [Select]
#pragma managed

#pragma once
using namespace System;
using namespace System::Text;
using namespace Autodesk::AutoCAD::DatabaseServices;
using namespace Autodesk::AutoCAD::Geometry;
using namespace Autodesk::AutoCAD::Runtime;


inline Autodesk::AutoCAD::DatabaseServices::SubentityId ToSubentityId(const AcDbSubentId& id)
{
  Autodesk::AutoCAD::DatabaseServices::SubentityId ret;
  GETSUBENTITYID(ret) = id;
  return ret;
}

namespace AcMgdWrprs
{
  public ref class Utilities
  {
     ....
    static bool SolidSubentitySelector([Out]ObjectId %solid,[Out]array<SubentityId> ^%sids);
  };
}

.cpp
Code: [Select]

#include "StdAfx.h"
#include "Utilities.h"

using namespace System;
using namespace System::Text;
using namespace System::Collections::Generic;
using namespace Autodesk::AutoCAD::DatabaseServices;
using namespace Autodesk::AutoCAD::Geometry;
using namespace Autodesk::AutoCAD::Runtime;
using namespace Autodesk::AutoCAD::EditorInput;
using namespace Autodesk::AutoCAD::ApplicationServices;

//
namespace AcMgdWrprs
{
  Utilities::Utilities(void)
  {
  }

  ...


  bool Utilities::SolidSubentitySelector( [Out]ObjectId %solid,[Out]array<SubentityId> ^%sids )
  {

    AcDbObjectId entId = NULL;
    AcDb3dSolid *pSolid3d = NULL;
    AcArray<AcDbSubentId*> subIdArr;
    AcEdSolidSubentitySelector subSel;

    if (subSel.selectFaces(entId, subIdArr) == Acad::eOk)
    {
      sids = gcnew array<SubentityId>(subIdArr.length());
      for (int i = 0; i < subIdArr.length(); i++)
      {
        AcDbSubentId *sid = subIdArr[i];
        sids[i] = ToSubentityId(*sid);
        delete sid;
      }
      solid = ToObjectId(entId);
      return true;
    }
    return false;
  }
}
Title: Re: how to move a face in a solid ?
Post by: It's Alive! on June 20, 2010, 10:33:34 PM

C#
Code: [Select]
namespace ExecMethod
{
  public class Commands
  {
    //++--
    [CommandMethod("doit")]
    static public void test()
    {
      Editor ed = AcAp.Application.DocumentManager.MdiActiveDocument.Editor;

      SubentityId[] sids;
      ObjectId id;
      if(AcMgdWrprs.Utilities.SolidSubentitySelector(out id, out sids))
      {
        ed.WriteMessage("\n{0}", id);

        foreach (SubentityId sid in sids)
          ed.WriteMessage("\n{0}", sid.Index);
     
      }
    }
  }
}
Title: Re: how to move a face in a solid ?
Post by: It's Alive! on June 20, 2010, 10:55:56 PM
here is extrudeFaces

Code: [Select]
bool Utilities::SolidExtrudeFaces( double width, double taper )
  {
    AcDbObjectId entId = NULL;
    AcDb3dSolid *pSolid3d = NULL;
    AcArray<AcDbSubentId*> subIdArr;
    AcEdSolidSubentitySelector subSel;

    if (subSel.selectFaces(entId, subIdArr) == Acad::eOk)
    {
      AcDbObjectPointer<AcDb3dSolid> spr(entId,AcDb::kForWrite);
      spr->extrudeFaces(subIdArr,width,taper);
      for (int i = 0; i < subIdArr.length(); i++)
      {
        AcDbSubentId *sid = subIdArr[i];
        delete sid;
      }
      return true;
    }
    return false;
  }

Code: [Select]
   [CommandMethod("doit")]
    static public void test()
    {
      Editor ed = AcAp.Application.DocumentManager.MdiActiveDocument.Editor;
      if (AcMgdWrprs.Utilities.SolidExtrudeFaces(10, 0))
      {
       
      }
    }

Title: Re: how to move a face in a solid ?
Post by: It's Alive! on June 20, 2010, 10:57:32 PM
doh, I see that was already in the API  :laugh:
Title: Re: how to move a face in a solid ?
Post by: ahlzl on June 20, 2010, 11:11:54 PM
Daniel is too great!

I would like to come to Shanghai (I was born in Shanghai),When to ask for Danie ARX,But I do not speak English! :oops: :oops: :oops:
Title: Re: how to move a face in a solid ?
Post by: SEANT on June 21, 2010, 04:11:16 AM
Thanks for the effort guys.  When I do make the jump, the study material here - and the other forum sections - will undoubtedly make the water more inviting. :-)



doh, I see that was already in the API  :laugh:

It is in the .NET API,  but the method appeared to be setup incorrectly (AutoCAD 2009).  I'll look through these later posts for clues to a straight .NET solution.  I certainly wouldn't be surprised if I missed something on my first go.