// // (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 System.Drawing; using System.Drawing.Drawing2D; using System.Collections.ObjectModel; using Autodesk.Revit.DB; namespace Revit.SDK.Samples.ObjectViewer.CS { /// /// provide methods to draw objects /// public class Sketch3D { // ratio of margin to canvas width private const float MarginRatio = 0.1f; // every frame distance to move the drawing private const float MoveDistance = 2.0f; // every frame ratio to zoom the drawing private const float ZoomRatio = 0.05f; // scale ratio of region showing the 2D geometry to the whole display region private const float Drawing2DScale2D = 0.5f; private List m_curve3Ds = new List(); // all 3D points private List m_curve2Ds = new List(); // all 2D points private Graphics3DData m_data3D; // 3D graphic data private Graphics2DData m_data2D; // 2D graphic data // defines a local geometric transform private Matrix m_transform3D; private Matrix m_transform2D; // displayed BoundingBox in canvas private RectangleF m_displayBBox = new RectangleF(0.0f, 0.0f, 10.0f, 10.0f); /// /// Displayed BoundingBox in canvas /// public RectangleF DisplayBBox { get { return m_displayBBox; } set { m_displayBBox = value; Calculate2DTransform(); Calculate3DTransform(); UpdateDataAndEvent(); if (null != UpdateViewEvent) { UpdateViewEvent(); } } } /// /// 2D graphic data /// public Graphics2DData Data2D { get { return m_data2D; } set { m_data2D = value; Calculate2DTransform(); UpdateDataAndEvent(); if (null != UpdateViewEvent) { UpdateViewEvent(); } } } /// /// 3D graphic data /// public Graphics3DData Data3D { get { return m_data3D; } set { m_data3D = value; UpdateDataAndEvent(); if (null != UpdateViewEvent) { UpdateViewEvent(); } } } /// /// view data update /// public event UpdateViewDelegate UpdateViewEvent; /// /// The default constructor /// /// a list contain all the 3d data /// a list contain all the 3d data public Sketch3D(Graphics3DData data3D, Graphics2DData data2D) { m_data3D = data3D; m_data2D = data2D; Calculate3DTransform(); Calculate2DTransform(); UpdateDataAndEvent(); } /// /// draw the line contain in m_lines in 2d Preview /// /// Graphics to draw /// public void Draw(Graphics graphics) { if (m_data3D.NumPoints == 0 && m_data2D.NumPoints == 0) { graphics.Clear(System.Drawing.Color.LightGray); return; } graphics.Clear(System.Drawing.Color.White); DrawCurves(graphics, m_curve3Ds, m_transform3D, new Pen(System.Drawing.Color.DarkGreen)); DrawCurves(graphics, m_curve2Ds, m_transform2D, new Pen(System.Drawing.Color.DarkBlue)); if (m_data2D.NumPoints > 0) { GraphicsPath gPath = new GraphicsPath(); gPath.AddRectangle(m_data2D.BBox); gPath.Transform(m_transform2D); SolidBrush brush = new SolidBrush(System.Drawing.Color.FromArgb(70, System.Drawing.Color.LightSkyBlue)); graphics.FillPath(brush, gPath); } } /// /// zoom the displayed drawing in canvas /// /// true for zoom in, false for zoom out public void Zoom(bool zoomIn) { if (zoomIn) { m_transform3D.Scale(1f - ZoomRatio, 1f - ZoomRatio, MatrixOrder.Append); } else { m_transform3D.Scale(1f + ZoomRatio, 1f + ZoomRatio, MatrixOrder.Append); } if (null != UpdateViewEvent) { UpdateViewEvent(); } } /// /// move view in horizontal direction /// /// left or right public void MoveX(bool left) { if (left) { m_transform3D.Translate(-MoveDistance, 0, MatrixOrder.Append); } else { m_transform3D.Translate(MoveDistance, 0, MatrixOrder.Append); } if (null != UpdateViewEvent) { UpdateViewEvent(); } } /// /// move view in vertical direction /// /// up or down public void MoveY(bool up) { if (up) { m_transform3D.Translate(0, MoveDistance, MatrixOrder.Append); } else { m_transform3D.Translate(0, -MoveDistance, MatrixOrder.Append); } if (null != UpdateViewEvent) { UpdateViewEvent(); } } /// /// update drawing data according and related Event /// private void UpdateDataAndEvent() { Initialize3DData(); Initialize2DData(); m_data3D.UpdateViewEvent += new UpdateViewDelegate(DataUpdateViewEvent); } /// /// initialize 3D drawing data /// private void Initialize3DData() { m_curve3Ds.Clear(); foreach (List vectors in m_data3D.ConstCurves) { PointF[] pnts = new PointF[vectors.Count]; m_curve3Ds.Add(pnts); for (int i = 0; i < vectors.Count; i++) { pnts[i] = new PointF((float)vectors[i].X, (float)vectors[i].Y); } } } /// /// initialize 2D drawing data /// private void Initialize2DData() { m_curve2Ds.Clear(); m_curve2Ds.AddRange(m_data2D.ConstCurves); } /// /// Draw curves /// /// graphics handle /// curves to be drawn /// transform between canvas and graphic data /// pen to draw private void DrawCurves(Graphics graphics, List curves, Matrix transform, Pen pen) { foreach (PointF[] curve in curves) { GraphicsPath gPath = new GraphicsPath(); if (curve.Length == 0) { break; } if (curve.Length == 1) { gPath.AddArc(new RectangleF(curve[0], new SizeF(0.5f, 0.5f)), 0.0f, (float)Math.PI); } else { gPath.AddLines(curve); } gPath.Transform(transform); graphics.DrawPath(pen, gPath); } } /// /// update according data when 3D data is updated /// private void DataUpdateViewEvent() { Initialize3DData(); if (null != UpdateViewEvent) { UpdateViewEvent(); } } /// /// calculate the transform between canvas and 3D geometry objects /// private void Calculate3DTransform() { float previewWidth = m_displayBBox.Width; float previewHeight = m_displayBBox.Height; RectangleF bbox3D = ToBoundingBox2D(m_data3D.BBox); PointF[] plgpts3D = CalculateCanvasRegion(previewWidth, previewHeight, bbox3D); m_transform3D = new Matrix(PreTreatBBox(bbox3D), plgpts3D); } /// /// calculate the transform between canvas and 2D geometry objects /// private void Calculate2DTransform() { float previewWidth = m_displayBBox.Width; float previewHeight = m_displayBBox.Height; float previewWidth2D = previewWidth * Drawing2DScale2D; float previewHeight2D = previewHeight * Drawing2DScale2D; PointF[] plgpts2D = CalculateCanvasRegion(previewWidth2D, previewHeight2D, m_data2D.BBox); m_transform2D = new Matrix(PreTreatBBox(m_data2D.BBox), plgpts2D); } /// /// pretreat BBoundingBox so that its height or width will be bigger than zero /// /// /// private RectangleF PreTreatBBox(RectangleF bbox) { const float EpsilonLen = 0.001f; if (bbox.Height < EpsilonLen) { bbox.Height = 0.001f; } if (bbox.Width < EpsilonLen) { bbox.Width = 0.001f; } return bbox; } /// /// get the display region, adjust the proportion and location /// /// upper-left, upper-right, and lower-left corners of the rectangle private PointF[] CalculateCanvasRegion(float previewWidth, float previewHeigh, RectangleF bbox) { // get the area without margin float realWidth = previewWidth * (1 - 2 * MarginRatio); float realHeight = previewHeigh * (1 - 2 * MarginRatio); float minX = previewWidth * MarginRatio; float minY = previewHeigh * MarginRatio; // ratio of width to height float originRate = bbox.Width / bbox.Height; float displayRate = realWidth / realHeight; if (originRate > displayRate) { // display area in canvas need move to center in height float goalHeight = realWidth / originRate; minY = minY + (realHeight - goalHeight) / 2; realHeight = goalHeight; } else { // display area in canvas need move to center in width float goalWidth = realHeight * originRate; minX = minX + (realWidth - goalWidth) / 2; realWidth = goalWidth; } PointF[] plgpts = new PointF[3]; plgpts[0] = new PointF(minX, realHeight + minY); // upper-left point plgpts[1] = new PointF(realWidth + minX, realHeight + minY); // upper-right point plgpts[2] = new PointF(minX, minY); // lower-left point return plgpts; } /// /// Get X and Y data of 3D BoundingBox to creat a 2D BoundingBox /// /// /// private RectangleF ToBoundingBox2D(BoundingBoxXYZ bbox3D) { float left = (float)(bbox3D.Min.X); float up = (float)(bbox3D.Min.Y); float right = (float)(bbox3D.Max.X); float down = (float)(bbox3D.Max.Y); RectangleF bbox2D = new RectangleF(left, up, (right - left), (down - up)); return bbox2D; } } }