Hi all,
I have been playing around with Fair59 hack for a while but got stuck. What I am trying to achieve is:
- make method possible to use in 3d and for wall different wall orientation
- adjust hatches on side faces of the wall especially on openings
The first objective seems to be accomplished. I am able to create ref planes from 3d view. The other however seems more tricky.
First of all, when I got the face from the opening even if it is painted it is returning Category Material (id 24) obviously no hatch pattern so no further action. I did a small workaround and set the category parameter to be one with hatch. In this case, I am able to create ref planes for those faces. Unfortunately, I can't align those references with hatch ref down the road. Please help I run out of ideas on how to approach it.
The snippet got quite long but it should work pasted into command. All utilities included in the region.
[Transaction(TransactionMode.Manual)]
[Regeneration(RegenerationOption.Manual)]
public class WallHatch : IExternalCommand
{
#region Command
public Result Execute(
ExternalCommandData commandData,
ref string message,
ElementSet elements
)
{
var uiapp = commandData?.Application;
var uidoc = uiapp?.ActiveUIDocument;
var app = uiapp?.Application;
var doc = uidoc?.Document;
List<Tuple<int, Reference, XYZ, XYZ>> res = new List<Tuple<int, Reference, XYZ, XYZ>>();
Selection _selection = uidoc.Selection;
ICollection<ElementId> selIds = _selection.GetElementIds();
List<Element> elems = new List<Element>();
foreach (var id in selIds)
{
elems.Add(doc.GetElement(id));
}
List<PlanarFace> wallFaces = GetSideFaces(elems[0] as Wall);
var WallHatchLine = AnalyzeWallHatch(doc,wallFaces);
List<PlanarFace> WallFront = GetWallMainFaces(elems[0], ShellLayerType.Exterior);
XYZ corner = GetPointAtCorner(WallFront.FirstOrDefault());
try
{
using (TransactionGroup tg = new TransactionGroup(doc, "align2RefPlane"))
{
tg.Start();
foreach (var HLine in WallHatchLine)
{
ReferencePlane pl = null;
//Ctreate Ref Plane from Hatch
using (Transaction t = new Transaction(doc, "CreateRefPlane"))
{
t.Start();
pl = doc.Create.NewReferencePlane(HLine.Item3.Add(HLine.Item4.Multiply(3)), HLine.Item3, HLine.Item5.Multiply(3), doc.ActiveView);
pl.Name = string.Format("{0}_{1}", "ref", Guid.NewGuid());
t.Commit();
}
//Align Ref Plane to Hatch Ref
string stableRef = string.Format("{0}:0:{1}", pl.UniqueId, "SURFACE");
Reference ref2Plane = Reference.ParseFromStableRepresentation(doc, stableRef);
using (Transaction t = new Transaction(doc, "align2RefPlane"))
{
t.Start();
doc.Create.NewAlignment(doc.ActiveView, ref2Plane, HLine.Item2);
t.Commit();
}
//Move Hatch
using (Transaction t = new Transaction(doc, "MovePlane"))
{
t.Start();
XYZ translation = HLine.Item3.Subtract(corner);
ElementTransformUtils.MoveElement(doc, pl.Id, translation);
t.Commit();
}
}
tg.Assimilate();
}
return Result.Succeeded;
}
catch (System.Exception e)
return Result.Failed;
}
}
#endregion Command
#region Utils
/// <summary>
/// Get Side Faces of the wall
/// </summary>
/// <param name="wall"></param>
/// <returns></returns>
/// <summary>
/// Analyse the hatch
/// </summary>
/// <param name="elem">An element with surface hatch.</param>
/// <param name="hatchface">The reference to the face to analyse.</param>
/// <returns>Returns:
/// foreach hatchline in hatchpattern
/// item1 = index of hatchline in hatchpattern
/// item2 = reference to the hatchline
/// item3 = point on hatchline
/// item4 = direction of hatchline.</returns>
/// item5 = face normal
//added item5 for debug reasons
List<Tuple<int, Reference, XYZ, XYZ, XYZ>> AnalyzeWallHatch(Document doc, List<PlanarFace> wallFaces) {
List<Tuple<int, Reference, XYZ, XYZ, XYZ>> res = new List<Tuple<int, Reference, XYZ, XYZ, XYZ>>();
List<XYZ> faceCorners = new List<XYZ>();
foreach (PlanarFace face in wallFaces)
{
Material mat = doc.GetElement(face.MaterialElementId) as Material;
//In no hatch pattern assgined
FillPatternElement patterntype = doc.GetElement(mat.SurfaceForegroundPatternId) as FillPatternElement;
if (patterntype == null) continue;
FillPattern pattern = patterntype.GetFillPattern();
// if (pattern.IsSolidFill || pattern.Target == FillPatternTarget.Drafting) continue;
// get number of gridLines in pattern
// grid count indicated no of direction in hatch pattern
// null - no hatch, 1 one direction (vertical or horizontal), 2 two directions , 3 directions
int _gridCount = pattern.GridCount;
// construct StableRepresentations and find the Reference to face - info for further hack
string StableRef = face.Reference.ConvertToStableRepresentation(doc);
using (var transaction = new Transaction(doc, "Wall align hatch"))
{
if (!doc.IsModifiable)
{
transaction.Start();
}
//Get Reff arrays from hatch directions indicates line of the hatch
for (int hatchDirectionIndex = 0; hatchDirectionIndex < _gridCount; hatchDirectionIndex++)
{
ReferenceArray _resArr = new ReferenceArray();
for (int ip = 0; ip < 2; ip++)
{
int index = (hatchDirectionIndex + 1) + (ip * _gridCount * 2);
//Merege string reperenetation of hatch to get hatch line reference from face hatch
string StableHatchString = StableRef + string.Format("/{0}", index);
Reference HatchRef = null;
try
{
HatchRef = Reference.ParseFromStableRepresentation(doc, StableHatchString);
}
catch
{ }
if (HatchRef == null) continue;
_resArr.Append(HatchRef);
}
// 2 or more References => create dimension
if (_resArr.Size > 1)
{
using (SubTransaction st = new SubTransaction(doc))
{
//setup sketch plane to be face
st.Start();
SketchPlane skpPln = SketchPlane.Create(doc, face.Reference);
doc.ActiveView.SketchPlane = skpPln;
XYZ othercorner = GetPointAtOpositeCorner(face);
Curve LowestEdge = GetLowestEdge(face);
XYZ dirvec = LowestEdge.GetEndPoint(1).Subtract(LowestEdge.GetEndPoint(0));
//Modified dimension line to be lower edge of the face
Dimension _dimension = doc.Create.NewDimension(doc.ActiveView, Line.CreateBound(LowestEdge.GetEndPoint(0), LowestEdge.GetEndPoint(1)), _resArr);
//seems not working in 3d view
// move dimension a tiny amount to orient the dimension perpendicular to the hatchlines
// I can't say why it works, but it does.
//ElementTransformUtils.MoveElement(doc, _dimension.Id, new XYZ(.1, 0, 0));
// ElementTransformUtils.MoveElement(doc, _dimension.Id, dirvec.Normalize()*20);
Reference r1 = _dimension.References.get_Item(0);
XYZ direction = (_dimension.Curve as Line).Direction;
XYZ hatchDirection = direction.CrossProduct(face.FaceNormal).Normalize();
XYZ origin = _dimension.Origin.Subtract(direction.Multiply((double)_dimension.Value / 2));
res.Add(new Tuple<int, Reference, XYZ, XYZ, XYZ>(hatchDirectionIndex, r1, origin, hatchDirection, face.FaceNormal));
//st.RollBack();
st.Commit();
}
}
}
transaction.Commit();
}
}
return res;
}
public static List<PlanarFace> GetWallMainFaces(Element elem, ShellLayerType faceside = ShellLayerType.Exterior)
{
List<PlanarFace> wallFaces = new List<PlanarFace>();
Wall wall = elem as Wall;
IList<Reference> rs = HostObjectUtils.GetSideFaces(wall, faceside);
foreach (Reference r in rs)
{
wallFaces.Add(elem.GetGeometryObjectFromReference(r) as PlanarFace);
}
return wallFaces;
}
public static List<PlanarFace> GetSideFaces(Wall wall)
{
List<PlanarFace> lineAngl = new List<PlanarFace>();
LocationCurve locationCurve = wall.Location as LocationCurve;
Line line = locationCurve.Curve as Line;
XYZ locVector = line.Direction;
List<PlanarFace> wallfaces = GetFaces(wall);
foreach (PlanarFace f in wallfaces)
{
XYZ faceNorm = f.ComputeNormal(new UV(0.5, 0.5));
if (IsParallel(locVector, faceNorm) == true)
{
lineAngl.Add(f);
}
}
return lineAngl;
}
public static List<PlanarFace> GetFaces(Wall wall)
{
List<PlanarFace> faclst = new List<PlanarFace>();
Options opt = new Options();
opt.ComputeReferences = true;
opt.DetailLevel = ViewDetailLevel.Fine;
opt.IncludeNonVisibleObjects = true;
GeometryElement geomElem = wall.get_Geometry(opt);
foreach (GeometryObject geomObj in geomElem)
{
Solid geomSolid = geomObj as Solid;
if (null != geomSolid)
{
int faces = 0;
double totalArea = 0;
foreach (PlanarFace geomFace in geomSolid.Faces)
{
faclst.Add(geomFace);
}
}
}
return faclst;
}
const double _eps = 1.0e-9;
public static bool IsParallel(XYZ a, XYZ b)
{
double angle = a.AngleTo(b);
return _eps > angle || System.Math.Abs(angle - System.Math.PI) < _eps;
}
public static XYZ GetPointAtCorner(PlanarFace face)
{
XYZ corner = null;
foreach (Curve cv in face.GetEdgesAsCurveLoops().FirstOrDefault())
{
corner = cv.GetEndPoint(0);
break;
}
return corner;
}
public static XYZ GetPointAtOpositeCorner(PlanarFace face)
{
XYZ corner = null;
foreach (Curve cv in face.GetEdgesAsCurveLoops().FirstOrDefault())
{
corner = cv.GetEndPoint(1);
break;
}
return corner;
}
public static Curve GetLowestEdge(PlanarFace face)
{
XYZ corner = null;
double midmin = xxx-xxxxxxxx;
Curve lowEdge = null;
foreach (Curve cv in face.GetEdgesAsCurveLoops().FirstOrDefault())
{
XYZ midpoint = (cv.GetEndPoint(0) + cv.GetEndPoint(1)) / 2;
if (midpoint.Z < midmin)
{
midmin = midpoint.Z;
lowEdge = cv;
}
}
return lowEdge;
}
}
}