// // (C) Copyright 2003-2019 by Autodesk, Inc. // // Permission to use, copy, modify, and distribute this software in // object code form for any purpose and without fee is hereby granted, // provided that the above copyright notice appears in all copies and // that both that copyright notice and the limited warranty and // restricted rights notice below appear in all supporting // documentation. // // AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS. // AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF // MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC. // DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE // UNINTERRUPTED OR ERROR FREE. // // Use, duplication, or disclosure by the U.S. Government is subject to // restrictions set forth in FAR 52.227-19 (Commercial Computer // Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii) // (Rights in Technical Data and Computer Software), as applicable. // using System; using System.Collections.Generic; using System.Text; using Autodesk.Revit.DB; using System.Drawing; using System.Drawing.Drawing2D; namespace Revit.SDK.Samples.NewHostedSweep.CS { /// /// This class is intent to display element's wire-frame with C# GDI. /// It contains a solid and a bounding box of an element. /// It also contains transformation (translation, rotation and scale) to /// transform the geometry edges. /// public class ElementGeometry { /// /// Element's Solid /// private Solid m_solid; /// /// Solid bounding box minimal corner. /// private XYZ m_bBoxMin; /// /// Solid bounding box maximal corner. /// private XYZ m_bBoxMax; /// /// Translation transform, it is intent to translate the solid. /// It is actually the center of Bounding box. /// private XYZ m_translation; /// /// Scale transform, it is intent to scale the solid. /// private double m_scale; /// /// Rotation transform, it is intent to rotate the solid. /// private Transform m_rotation; /// /// If the solid is transformed (includes translation, scale and rotation) /// this flag should be true, otherwise false. /// private bool m_isDirty; /// /// Solid's Edge to EdgeBinding dictionary. It is intent to store all the edges /// of solid. /// private Dictionary m_edgeBindinDic; /// /// Translation transform, it is intent to translate the solid. /// It is actually the center of Bounding box. /// public XYZ Translation { get { return m_translation; } set { m_isDirty = true; m_translation = value; } } /// /// Scale transform, it is intent to scale the solid. /// public double Scale { get { return m_scale; } set { m_isDirty = true; m_scale = value; } } /// /// Rotation transform, it is intent to rotate the solid. /// public Transform Rotation { get { return m_rotation; } set { m_isDirty = true; m_rotation = value; } } /// /// Element's Solid /// public Solid Solid { get { return m_solid; } } /// /// Solid's Edge to EdgeBinding dictionary. It is intent to store all the edges /// of solid. /// public Dictionary EdgeBindingDic { get { return m_edgeBindinDic; } } /// /// Constructor, Construct a new object with an element's geometry Solid, /// and its corresponding bounding box. /// /// Element's geometry Solid /// Element's geometry bounding box public ElementGeometry(Solid solid, BoundingBoxXYZ box) { m_solid = solid; m_bBoxMin = box.Min; m_bBoxMax = box.Max; m_isDirty = true; // Initialize edge binding m_edgeBindinDic = new Dictionary(); foreach (Edge edge in m_solid.Edges) { EdgeBinding edgeBingding = new EdgeBinding(edge); m_edgeBindinDic.Add(edge, edgeBingding); } } /// /// Initialize the transform (includes translation, scale, and rotation). /// /// Width of the view /// Height of the view public void InitializeTransform(double width, double height) { // Initialize translation and rotation transform XYZ bBoxCenter = (m_bBoxMax + m_bBoxMin) / 2.0; m_translation = -bBoxCenter; m_rotation = Transform.Identity; // Initialize scale factor double bBoxWidth = m_bBoxMax.X - m_bBoxMin.X; double bBoxHeight = m_bBoxMax.Y - m_bBoxMin.Y; double widthScale = width / bBoxWidth; double heigthScale = height / bBoxHeight; m_scale = Math.Min(widthScale, heigthScale); // Set dirty flag m_isDirty = true; } /// /// Reset all the edges' status to their original status. /// public void ResetEdgeStates() { foreach (KeyValuePair pair in m_edgeBindinDic) { pair.Value.Reset(); } } /// /// Update all the edges' transform (include translation, scale, and rotation), /// reconstruct the edge's geometry info. /// private void Update() { if (!m_isDirty) return; foreach (KeyValuePair pair in m_edgeBindinDic) { pair.Value.Update(m_rotation, m_translation, m_scale); } m_isDirty = false; } /// /// Draw all the edges of solid in Graphics. /// /// Graphics, edges will be draw in it public void Draw(Graphics g) { Update(); foreach (KeyValuePair pair in m_edgeBindinDic) { pair.Value.Draw(g); } } } /// /// Binds an edge with some properties which contains its geometry information /// and indicates whether the edge is selected or highlighted. /// public class EdgeBinding : IDisposable { /// /// Edge points in world coordinate system of Revit. /// private IList m_points; /// /// Edge geometry presentation in C# GDI. /// private GraphicsPath m_gdiEdge; /// /// Edge bounding Region used to hit testing. /// private Region m_region; /// /// Pen for edge display. /// private Pen m_pen; /// /// A flag to indicate the edge is highlighted or not. /// private bool m_isHighLighted; /// /// A flag to indicate the edge is selected or not. /// private bool m_isSelected; /// /// Gets whether the edge is highlighted or not. /// public bool IsHighLighted { get { return m_isHighLighted; } set { m_isHighLighted = value; } } /// /// Gets whether the edge is selected or not. /// public bool IsSelected { get { return m_isSelected; } set { m_isSelected = value; } } /// /// Constructor takes Edge as parameter. /// /// Edge public EdgeBinding(Edge edge) { m_points = edge.Tessellate(); m_pen = new Pen(System.Drawing.Color.White); Reset(); } /// /// Reset the status of the edge: un-highlighted, un-selected /// public void Reset() { m_isHighLighted = false; m_isSelected = false; m_region = null; } /// /// Update the edge's geometry according to the transformation. /// /// Rotation transform /// Translation transform /// Scale transform public void Update(Transform rotation, XYZ translation, double scale) { rotation = rotation.Inverse; PointF[] points = new PointF[m_points.Count]; for (int i = 0; i < m_points.Count; i++) { XYZ tmpPt = m_points[i]; tmpPt = rotation.OfPoint((tmpPt + translation) * scale); points[i] = new PointF((float)tmpPt.X, (float)tmpPt.Y); } if (m_gdiEdge != null) m_gdiEdge.Dispose(); m_gdiEdge = new GraphicsPath(); m_gdiEdge.AddLines(points); if (m_region != null) m_region.Dispose(); m_region = null; } /// /// Draw the edge in Graphics. /// /// Graphics public void Draw(Graphics g) { m_pen.Width = 2.0f; if (m_isHighLighted) { m_pen.Color = System.Drawing.Color.Yellow; } else if (m_isSelected) { m_pen.Color = System.Drawing.Color.Red; } else { m_pen.Color = System.Drawing.Color.Green; } g.DrawPath(m_pen, m_gdiEdge); } /// /// Return the Edge Region. /// /// Region of the edge private Region GetRegion() { if (m_region == null) { GraphicsPath tmpPath = new GraphicsPath(); tmpPath.AddLines(m_gdiEdge.PathPoints); Pen tmpPen = new Pen(System.Drawing.Color.White, 3.0f); tmpPath.Widen(tmpPen); m_region = new Region(tmpPath); } return m_region; } /// /// Test whether or not the edge is under a specified location. /// /// X coordinate /// Y coordinate /// private bool HitTest(float x, float y) { return this.GetRegion().IsVisible(x, y); } /// /// If the edge under the location (x, y), set the highlight flag to true, /// otherwise false. /// /// X coordinate /// Y coordinate /// public bool HighLight(float x, float y) { m_isHighLighted = HitTest(x, y); return m_isHighLighted; } #region IDisposable Members /// /// Dispose /// public void Dispose() { m_gdiEdge.Dispose(); m_pen.Dispose(); m_region.Dispose(); } #endregion } }