Thanks for the reply Kerry and your advice sounds good.
I've broken it into 2 functions
[CommandMethod("ChangeInsertionpoint")]
public static void NewInsertionPoint()
{
Document doc = acadApp.DocumentManager.MdiActiveDocument;
Editor ed = doc.Editor;
Database db = doc.Database;
PromptEntityOptions peo = new PromptEntityOptions("Pick a blockreference to change the insertion point:");
peo.SetRejectMessage("Must be a blockreference:");
peo.AddAllowedClass (typeof(BlockReference),true);
PromptEntityResult per = ed.GetEntity(peo);
if (per.Status != PromptStatus.OK) return;
PromptPointResult ppo=ed.GetPoint("Pick the new insertion point:");
if (ppo.Status != PromptStatus.OK) return;
Matrix3d ucs=ed.CurrentUserCoordinateSystem;
Point3d insPt = ppo.Value.TransformBy(ucs),newPt;
using(Transaction tr = db.TransactionManager.StartTransaction())
{
BlockReference br = tr.GetObject(per.ObjectId, OpenMode.ForWrite) as BlockReference;
ObjectId blockId=br.BlockTableRecord;
BlockTableRecord btr = tr.GetObject(blockId, OpenMode.ForWrite) as BlockTableRecord;
Matrix3d blockmatrix = br.BlockTransform;
insPt= insPt.TransformBy(blockmatrix.Inverse());
Vector3d v=btr.Origin-insPt;
Matrix3d move=Matrix3d.Displacement(v);
foreach (ObjectId id in btr)
{
Entity ent = tr.GetObject(id, OpenMode.ForWrite) as Entity;
ent.TransformBy(move);
ent.DowngradeOpen();
}
ObjectIdCollection ids = btr.GetBlockReferenceIds(false, true);
foreach (ObjectId brefId in ids)
{
BlockReference b = tr.GetObject(brefId, OpenMode.ForWrite) as BlockReference;
newPt = insPt.TransformBy(b.BlockTransform);
if (b.BlockTableRecord == blockId)
{
b.TransformBy(Matrix3d.Displacement(newPt - b.Position));
}
else
{
BlockTableRecord nestedBref = tr.GetObject(b.BlockTableRecord, OpenMode.ForWrite) as BlockTableRecord;
foreach (ObjectId id in nestedBref)
{
if (id == brefId)
{
b.TransformBy(Matrix3d.Displacement(newPt - b.Position));
break;
}
}
}
UpdateClip(db, b, v);
}
tr.Commit();
}
ed.Regen();
} // end NewInsertionPoint
public static void UpdateClip(Database db,BlockReference br,Vector3d v)
{
const string spatialName = "SPATIAL";
ObjectId ExId = br.ExtensionDictionary;
if (ExId == ObjectId.Null) return;
using (Transaction tr = db.TransactionManager.StartTransaction())
{
DBDictionary dic = tr.GetObject(ExId, OpenMode.ForWrite) as DBDictionary;
if (!dic.Contains("ACAD_FILTER")) return;
DBDictionary clipDic = tr.GetObject(dic.GetAt("ACAD_FILTER"), OpenMode.ForWrite) as DBDictionary;
if (!clipDic.Contains(spatialName)) return;
SpatialFilter sf = tr.GetObject(clipDic.GetAt(spatialName), OpenMode.ForWrite) as SpatialFilter;
SpatialFilterDefinition def = sf.Definition;
Plane plane=new Plane(new Point3d(0,0,def.Elevation),def.Normal);
Matrix3d m = br.BlockTransform;
Matrix3d disp=Matrix3d.Displacement(v.Negate());
Point2dCollection pts = new Point2dCollection();
foreach (Point2d pt in def.GetPoints())
{
Point3d p = new Point3d(pt.X, pt.Y,def.Elevation);
p = p.TransformBy(m.Inverse());
pts.Add(p.Convert2d(plane));
}
SpatialFilterDefinition def2=new SpatialFilterDefinition(
pts,def.Normal,def.Elevation,def.FrontClip,def.BackClip,def.Enabled);
clipDic.Remove(spatialName);
SpatialFilter sf2 = new SpatialFilter();
sf2.Definition = def2;
SpatialFilterVolume sfv=sf.GetVolume();
clipDic.SetAt(spatialName, sf2);
tr.AddNewlyCreatedDBObject(sf2,true);
tr.Commit();
}
} // end UpdateClip
And This works the first time on most but the second time it throws a wobbler. Also if you clip a blockref then rotate it it gets it wrong, so the compound matrix needs to be one step back in time. In the beginning I thought I had to move the clip relative to the new insertion point but I don't .