Hi,
I almost got the solution for you i think with the help of "DirectContext3DService and the "DuplicateGraphics" SDK sample. The only thing i'm not happy with is that the first point is picked with "uidoc.Selection.PickPoint("Pick First Point:")" So you have the Revit Snap options but the second point is with a custom made "Click" code as you can see below. It would be nicer if the second point also can be done with a "uidoc.Selection.PickPoint("Pick Second Point:")" but if you use that the "transient" line doesn't update anymore in the while loop. Maybe @jeremy_tammik or @Chuong.Ho got a solution to make this last step work?
#region Namespaces
using Autodesk.Revit.ApplicationServices;
using Autodesk.Revit.Attributes;
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;
using Autodesk.Revit.UI.Selection;
using System;
using System.Collections.Generic;
using Autodesk.Revit.UI.Events;
using Autodesk.Revit.DB.ExternalService;
using System.Runtime.InteropServices;
using PointW = System.Drawing.Point;
using windowsForms = System.Windows.Forms;
using System.Threading;
using System.Diagnostics;
using System.Windows.Interop;
using System.ComponentModel;
using System.Reflection;
#endregion
namespace SharpLion_BIM_Toolbar
{
[Transaction(TransactionMode.Manual)]
[Regeneration(RegenerationOption.Manual)]
public class DuplicateGraphics : IExternalCommand
{
List<RevitElementDrawingServer> m_servers;
HashSet<Document> m_documents;
UIDocument m_UIdoc;
UIApplication m_uiApp;
private IntPtr rWindow = IntPtr.Zero;
XYZ m_P1;
private List<Line> LineList = new List<Line>();
public Result Execute(
ExternalCommandData commandData,
ref string message,
ElementSet elements)
{
UIApplication uiapp = commandData.Application;
UIDocument uidoc = uiapp.ActiveUIDocument;
Application app = uiapp.Application;
Document doc = uidoc.Document;
m_servers = new List<RevitElementDrawingServer>();
m_documents = new HashSet<Document>();
m_UIdoc = uidoc;
m_uiApp = uiapp;
this.rWindow = GetActiveWindow();
try
{
m_P1 = uidoc.Selection.PickPoint("Pick First Point:");
}
catch (Exception ex)
{
}
try
{
bool done = false;
while (!done)
{
windowsForms.Cursor.Current = windowsForms.Cursors.Cross;
AddRevitElementServer(m_UIdoc, m_P1);
var revitHandle = Process
.GetCurrentProcess().MainWindowHandle;
SetStatusText(revitHandle, "Pick Last Point:");
if (GetLeftMousePressed()|| GetEscPressed())
{
i++;
if (i == 2)
{
i = 0;
done = true;
unregisterServers(doc, true);
}
}
}
}
catch { }
return Result.Succeeded;
}
private int i = 0;
public void AddRevitElementServer(UIDocument uidoc,XYZ p1)
{
LineList.Clear();
XYZ p2 = GetMousePosition();
if (p1.DistanceTo(p2) > 0.01)
{
LineList.Add(Line.CreateBound(p1, p2));
m_uiApp.ActiveUIDocument.RefreshActiveView();
// XYZ p3 = m_UIdoc.Selection.PickPoint("Pick Last Point:");
// Create the server and register it with the DirectContext3D service.
ExternalService directContext3DService = ExternalServiceRegistry.GetService(ExternalServices.BuiltInExternalServices.DirectContext3DService);
RevitElementDrawingServer revitServer = new RevitElementDrawingServer(uidoc, LineList);
directContext3DService.AddServer(revitServer);
m_servers.Add(revitServer);
MultiServerService msDirectContext3DService = directContext3DService as MultiServerService;
IList<Guid> serverIds = msDirectContext3DService.GetActiveServerIds();
serverIds.Add(revitServer.GetServerId());
// Add the new server to the list of active servers.
msDirectContext3DService.SetActiveServers(serverIds);
m_documents.Add(uidoc.Document);
uidoc.UpdateAllOpenViews();
}
}
public void unregisterServers(Document document, bool updateViews)
{
ExternalServiceId externalDrawerServiceId = ExternalServices.BuiltInExternalServices.DirectContext3DService;
var externalDrawerService = ExternalServiceRegistry.GetService(externalDrawerServiceId) as MultiServerService;
if (externalDrawerService == null)
return;
foreach (var registeredServerId in externalDrawerService.GetRegisteredServerIds())
{
var externalDrawServer = externalDrawerService.GetServer(registeredServerId) as RevitElementDrawingServer;
if (externalDrawServer == null)
continue;
if (document != null && !document.Equals(externalDrawServer.Document))
continue;
externalDrawerService.RemoveServer(registeredServerId);
}
if (document != null)
{
m_servers.RemoveAll(server => document.Equals(server.Document));
if (updateViews)
{
UIDocument uidoc = new UIDocument(document);
uidoc.UpdateAllOpenViews();
}
m_documents.Remove(document);
}
else
{
m_servers.Clear();
if (updateViews)
foreach (var doc in m_documents)
{
UIDocument uidoc = new UIDocument(doc);
uidoc.UpdateAllOpenViews();
}
m_documents.Clear();
}
}
private XYZ GetMousePosition()
{
UIView uiview = GetActiveUiView(m_UIdoc);
Rectangle rect = uiview.GetWindowRectangle();
PointW p = windowsForms.Cursor.Position;
double dx = (double)(p.X - rect.Left)
/ (rect.Right - rect.Left);
double dy = (double)(p.Y - rect.Bottom)
/ (rect.Top - rect.Bottom);
IList<XYZ> corners = uiview.GetZoomCorners();
XYZ a = corners[0];
XYZ b = corners[1];
XYZ v = b - a;
XYZ q = a
+ dx * v.X * XYZ.BasisX
+ dy * v.Y * XYZ.BasisY;
return q;
}
static UIView GetActiveUiView(UIDocument uidoc)
{
Document doc = uidoc.Document;
View view = doc.ActiveView;
IList<UIView> uiviews = uidoc.GetOpenUIViews();
UIView uiview = null;
foreach (UIView uv in uiviews)
{
if (uv.ViewId.Equals(view.Id))
{
uiview = uv;
break;
}
}
return uiview;
}
[DllImport("user32.dll")]
static extern short GetAsyncKeyState(int VirtualKeyPressed);
public static bool GetLeftMousePressed()
{
if (GetAsyncKeyState(0x01) == 0)
return false;
else
return true;
}
public static bool GetEscPressed()
{
if (GetAsyncKeyState(0x1B) == 0)
return false;
else
return true;
}
//Set custom bottom text
[DllImport("user32.dll")]
static extern IntPtr GetActiveWindow();
[DllImport("user32.dll",
SetLastError = true,
CharSet = CharSet.Auto)]
private static extern int SetWindowText(
IntPtr hWnd,
string lpString);
[DllImport("user32.dll",
SetLastError = true)]
private static extern IntPtr FindWindowEx(
IntPtr hwndParent,
IntPtr hwndChildAfter,
string lpszClass,
string lpszWindow);
public static void SetStatusText(
IntPtr mainWindow,
string text)
{
var statusBar = FindWindowEx(
mainWindow, IntPtr.Zero,
"msctls_statusbar32", "");
if (statusBar != IntPtr.Zero) SetWindowText(statusBar, text);
}
}
}
And Second Class:
using Autodesk.Revit.UI;
using Autodesk.Revit.DB;
using Autodesk.Revit.DB.ExternalService;
using Autodesk.Revit.DB.DirectContext3D;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
namespace SharpLion_BIM_Toolbar
{
class RevitElementDrawingServer : IDirectContext3DServer
{
public RevitElementDrawingServer(UIDocument uiDoc, List<Line> line)
{
m_guid = Guid.NewGuid();
m_uiDocument = uiDoc;
// m_element = elem;
m_lines = line;
}
public System.Guid GetServerId() { return m_guid; }
public System.String GetVendorId() { return "ADSK"; }
public ExternalServiceId GetServiceId() { return ExternalServices.BuiltInExternalServices.DirectContext3DService; }
public System.String GetName() { return "Revit Element Drawing Server"; }
public System.String GetDescription() { return "Duplicates graphics from a Revit element."; }
// Corresponds to functionality that is not used in this sample.
public System.String GetApplicationId() { return ""; }
// Corresponds to functionality that is not used in this sample.
public System.String GetSourceId() { return ""; }
// Corresponds to functionality that is not used in this sample.
public bool UsesHandles() { return false; }
public bool CanExecute(Autodesk.Revit.DB.View view)
{
return true;
//var doc = view.Document;
//return doc.Equals(this.Document);
}
// Reports a bounding box of the geometry that this server submits for drawing.
public Outline GetBoundingBox(Autodesk.Revit.DB.View view)
{
return null;
}
// Indicates that this server will submit geometry during the rendering pass for transparent geometry.
public bool UseInTransparentPass(Autodesk.Revit.DB.View view) { return true; }
// Submits the geometry for rendering.
public void RenderScene(Autodesk.Revit.DB.View view, DisplayStyle displayStyle)
{
try
{
// Populate geometry buffers if they are not initialized or need updating.
if (m_nonTransparentFaceBufferStorage == null || m_nonTransparentFaceBufferStorage.needsUpdate(displayStyle) ||
m_transparentFaceBufferStorage == null || m_transparentFaceBufferStorage.needsUpdate(displayStyle) ||
m_edgeBufferStorage == null || m_edgeBufferStorage.needsUpdate(displayStyle))
{
CreateBufferStorageForElement(m_lines, displayStyle);
}
// Conditionally submit line segment primitives.
if (displayStyle != DisplayStyle.Shading &&
m_edgeBufferStorage.PrimitiveCount > 0)
DrawContext.FlushBuffer(m_edgeBufferStorage.VertexBuffer,
m_edgeBufferStorage.VertexBufferCount,
m_edgeBufferStorage.IndexBuffer,
m_edgeBufferStorage.IndexBufferCount,
m_edgeBufferStorage.VertexFormat,
m_edgeBufferStorage.EffectInstance, PrimitiveType.LineList, 0,
m_edgeBufferStorage.PrimitiveCount);
}
catch (Exception e)
{
MessageBox.Show(e.ToString());
}
}
// Initialize and populate buffers that hold graphics primitives, set up related parameters that are needed for drawing.
private void CreateBufferStorageForElement(List<Line> transientLines, DisplayStyle displayStyle)
{
m_edgeBufferStorage = new RenderingPassBufferStorage(displayStyle);
foreach (var edge in transientLines)
{
// if (edge.Length > 1e-06)
{
IList<XYZ> xyzs = edge.Tessellate();
m_edgeBufferStorage.VertexBufferCount += xyzs.Count;
m_edgeBufferStorage.PrimitiveCount += xyzs.Count - 1;
m_edgeBufferStorage.EdgeXYZs.Add(xyzs);
}
}
ProcessEdges(m_edgeBufferStorage);
}
// A helper function, analogous to ProcessFaces.
private void ProcessEdges(RenderingPassBufferStorage bufferStorage)
{
List<IList<XYZ>> edges = bufferStorage.EdgeXYZs;
if (edges.Count == 0)
return;
// Edges are encoded as line segment primitives whose vertices contain only position information.
bufferStorage.FormatBits = VertexFormatBits.Position;
int edgeVertexBufferSizeInFloats = VertexPosition.GetSizeInFloats() * bufferStorage.VertexBufferCount;
List<int> numVerticesInEdgesBefore = new List<int>();
numVerticesInEdgesBefore.Add(0);
bufferStorage.VertexBuffer = new VertexBuffer(edgeVertexBufferSizeInFloats);
bufferStorage.VertexBuffer.Map(edgeVertexBufferSizeInFloats);
{
VertexStreamPosition vertexStream = bufferStorage.VertexBuffer.GetVertexStreamPosition();
foreach (IList<XYZ> xyzs in edges)
{
foreach (XYZ vertex in xyzs)
{
vertexStream.AddVertex(new VertexPosition(vertex));
}
numVerticesInEdgesBefore.Add(numVerticesInEdgesBefore.Last() + xyzs.Count);
}
}
bufferStorage.VertexBuffer.Unmap();
int edgeNumber = 0;
bufferStorage.IndexBufferCount = bufferStorage.PrimitiveCount * IndexLine.GetSizeInShortInts();
int indexBufferSizeInShortInts = 1 * bufferStorage.IndexBufferCount;
bufferStorage.IndexBuffer = new IndexBuffer(indexBufferSizeInShortInts);
bufferStorage.IndexBuffer.Map(indexBufferSizeInShortInts);
{
IndexStreamLine indexStream = bufferStorage.IndexBuffer.GetIndexStreamLine();
foreach (IList<XYZ> xyzs in edges)
{
int startIndex = numVerticesInEdgesBefore[edgeNumber];
for (int i = 1; i < xyzs.Count; i++)
{
// Add two indices that define a line segment.
indexStream.AddLine(new IndexLine((int)(startIndex + i - 1),
(int)(startIndex + i)));
}
edgeNumber++;
}
}
bufferStorage.IndexBuffer.Unmap();
bufferStorage.VertexFormat = new VertexFormat(bufferStorage.FormatBits);
bufferStorage.EffectInstance = new EffectInstance(bufferStorage.FormatBits);
}
public Document Document
{
get { return (m_uiDocument != null) ? m_uiDocument.Document : null; }
}
private Guid m_guid;
private Element m_element;
private List<Line> m_lines;
private XYZ m_offset;
private UIDocument m_uiDocument;
private RenderingPassBufferStorage m_nonTransparentFaceBufferStorage;
private RenderingPassBufferStorage m_transparentFaceBufferStorage;
private RenderingPassBufferStorage m_edgeBufferStorage;
#region Helper classes
// A class that brings together all the data and rendering parameters that are needed to draw one sequence of primitives (e.g., triangles)
// with the same format and appearance.
class RenderingPassBufferStorage
{
public RenderingPassBufferStorage(DisplayStyle displayStyle)
{
DisplayStyle = displayStyle;
EdgeXYZs = new List<IList<XYZ>>();
}
public bool needsUpdate(DisplayStyle newDisplayStyle)
{
if (newDisplayStyle != DisplayStyle)
return true;
if (PrimitiveCount > 0)
if (VertexBuffer == null || !VertexBuffer.IsValid() ||
IndexBuffer == null || !IndexBuffer.IsValid() ||
VertexFormat == null || !VertexFormat.IsValid() ||
EffectInstance == null || !EffectInstance.IsValid())
return true;
return false;
}
public DisplayStyle DisplayStyle { get; set; }
public VertexFormatBits FormatBits { get; set; }
public List<IList<XYZ>> EdgeXYZs { get; set; }
public int PrimitiveCount { get; set; }
public int VertexBufferCount { get; set; }
public int IndexBufferCount { get; set; }
public VertexBuffer VertexBuffer { get; set; }
public IndexBuffer IndexBuffer { get; set; }
public VertexFormat VertexFormat { get; set; }
public EffectInstance EffectInstance { get; set; }
}
#endregion
}
}