Message 1 of 4
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report
I'm trying to write a command that draws revision clouds around selected elements. It works fine when revision clouds are drawn in the view, but when I'm trying to draw them on the sheet matching their corresponding locations of selected elements in view they don't land in correct locations. They get shifted in either direction by small amounts. Does anyone know why?
using Autodesk.Revit.Attributes;
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;
using System;
using System.Collections.Generic;
using System.Linq;
[Transaction(TransactionMode.Manual)]
public class CloudsToSheetParametric : IExternalCommand
{
public Result Execute(
ExternalCommandData cd,
ref string message,
ElementSet elements)
{
UIDocument uiDoc = cd.Application.ActiveUIDocument;
Document doc = uiDoc.Document;
View view = doc.ActiveView;
ICollection<ElementId> selIds = uiDoc.Selection.GetElementIds();
if (!selIds.Any())
{
message = "Select one or more elements first.";
return Result.Failed;
}
// ─── locate the viewport that shows the active view ───
ViewSheet sheet = null;
Viewport vp = null;
foreach (ViewSheet sh in new FilteredElementCollector(doc)
.OfClass(typeof(ViewSheet)))
{
foreach (ElementId vpid in sh.GetAllViewports())
{
vp = doc.GetElement(vpid) as Viewport;
if (vp != null && vp.ViewId == view.Id)
{
sheet = sh;
break;
}
}
if (sheet != null) break;
}
if (sheet == null)
{
message = "Active view is not placed on a sheet.";
return Result.Failed;
}
// ─── build parametric mapper ───
ParamMapper map;
try
{
map = new ParamMapper(view, vp);
}
catch (InvalidOperationException ex)
{
message = ex.Message;
return Result.Failed;
}
// ─── choose (or create) a revision ───
Revision rev = GetLatestVisibleRevision(doc);
if (rev == null)
{
using (Transaction t = new Transaction(doc, "Create Revision"))
{
t.Start();
rev = Revision.Create(doc);
t.Commit();
}
}
const double OFFSET = 0.2; // ft – halo
// ─── build sheet-space rectangles ───
var curves = new List<Curve>();
foreach (ElementId id in selIds)
{
Element el = doc.GetElement(id);
BoundingBoxXYZ bb = el.get_BoundingBox(view);
if (bb == null) continue;
// model-space bbox (expanded)
XYZ p1 = bb.Transform.OfPoint(bb.Min);
XYZ p2 = bb.Transform.OfPoint(bb.Max);
XYZ min = new XYZ(
Math.Min(p1.X, p2.X) - OFFSET,
Math.Min(p1.Y, p2.Y) - OFFSET, 0);
XYZ max = new XYZ(
Math.Max(p1.X, p2.X) + OFFSET,
Math.Max(p1.Y, p2.Y) + OFFSET, 0);
// sheet points via param mapper
XYZ bl = map.ToSheet(min); // bottom-left
XYZ tl = map.ToSheet(new XYZ(min.X, max.Y, 0)); // top-left
XYZ tr = map.ToSheet(max); // top-right
XYZ br = map.ToSheet(new XYZ(max.X, min.Y, 0)); // bottom-right
curves.Add(Line.CreateBound(bl, tl));
curves.Add(Line.CreateBound(tl, tr));
curves.Add(Line.CreateBound(tr, br));
curves.Add(Line.CreateBound(br, bl));
}
if (!curves.Any())
{
message = "No bounding boxes found.";
return Result.Failed;
}
// ─── create the cloud on the sheet ───
using (Transaction t = new Transaction(doc, "Revision Clouds"))
{
t.Start();
RevisionCloud.Create(doc, sheet, rev.Id, curves);
t.Commit();
}
return Result.Succeeded;
}
// ----------------------------------------------------------
// Helper: model → sheet via parametric crop-box coordinates
// ----------------------------------------------------------
private sealed class ParamMapper
{
private readonly Transform _toLocal; // model → crop-local
private readonly XYZ _minLoc; // crop local min
private readonly double _wLoc; // crop width (local)
private readonly double _hLoc; // crop height "
private readonly Outline _vpOutline; // viewport box on sheet
private readonly ViewportRotation _rot;
public ParamMapper(View v, Viewport vp)
{
BoundingBoxXYZ crop = v.CropBox;
if (crop == null || !v.CropBoxActive)
throw new InvalidOperationException("View lacks an active crop box.");
_toLocal = crop.Transform.Inverse;
_minLoc = crop.Min;
_wLoc = crop.Max.X - crop.Min.X;
_hLoc = crop.Max.Y - crop.Min.Y;
_vpOutline = vp.GetBoxOutline();
_rot = vp.Rotation;
}
public XYZ ToSheet(XYZ modelPt)
{
// 1) model → local crop coordinates
XYZ loc = _toLocal.OfPoint(modelPt);
// 2) parametric position inside crop
double u = (loc.X - _minLoc.X) / _wLoc; // 0 … 1
double v = (loc.Y - _minLoc.Y) / _hLoc;
// 3) viewport size
double wSheet = _vpOutline.MaximumPoint.X - _vpOutline.MinimumPoint.X;
double hSheet = _vpOutline.MaximumPoint.Y - _vpOutline.MinimumPoint.Y;
// 4) map (u,v) to sheet, honour 90° rotations
double sx, sy;
switch (_rot)
{
case ViewportRotation.None:
sx = u * wSheet;
sy = v * hSheet;
break;
case ViewportRotation.Clockwise:
sx = v * wSheet;
sy = (1.0 - u) * hSheet;
break;
case ViewportRotation.Counterclockwise:
sx = (1.0 - v) * wSheet;
sy = u * hSheet;
break;
default:
throw new InvalidOperationException("Unsupported viewport rotation.");
}
return new XYZ(
_vpOutline.MinimumPoint.X + sx,
_vpOutline.MinimumPoint.Y + sy,
0);
}
}
private static Revision GetLatestVisibleRevision(Document doc)
{
return new FilteredElementCollector(doc)
.OfClass(typeof(Revision))
.Cast<Revision>()
.Where(r => r.Visibility != RevisionVisibility.Hidden)
.OrderByDescending(r => r.SequenceNumber)
.FirstOrDefault();
}
}
Solved! Go to Solution.