1+ using System . Diagnostics . CodeAnalysis ;
2+
3+ namespace RevitDevTool . Visualization . Helpers ;
4+
5+ public static class RenderGeometryHelper
6+ {
7+ public static List < List < XYZ > > GetSegmentationTube ( IList < XYZ > vertices , double diameter )
8+ {
9+ var points = new List < List < XYZ > > ( ) ;
10+
11+ for ( var i = 0 ; i < vertices . Count ; i ++ )
12+ {
13+ var center = vertices [ i ] ;
14+ XYZ normal ;
15+ if ( i == 0 )
16+ {
17+ normal = ( vertices [ i + 1 ] - center ) . Normalize ( ) ;
18+ }
19+ else if ( i == vertices . Count - 1 )
20+ {
21+ normal = ( center - vertices [ i - 1 ] ) . Normalize ( ) ;
22+ }
23+ else
24+ {
25+ normal = ( ( vertices [ i + 1 ] - vertices [ i - 1 ] ) / 2.0 ) . Normalize ( ) ;
26+ }
27+
28+ points . Add ( TessellateCircle ( center , normal , diameter / 2 ) ) ;
29+ }
30+
31+ return points ;
32+ }
33+
34+ public static XYZ GetMeshVertexNormal ( Mesh mesh , int index , DistributionOfNormals normalDistribution )
35+ {
36+ switch ( normalDistribution )
37+ {
38+ case DistributionOfNormals . AtEachPoint :
39+ return mesh . GetNormal ( index ) ;
40+ case DistributionOfNormals . OnEachFacet :
41+ var vertex = mesh . Vertices [ index ] ;
42+ for ( var i = 0 ; i < mesh . NumTriangles ; i ++ )
43+ {
44+ var triangle = mesh . get_Triangle ( i ) ;
45+ var triangleVertex = triangle . get_Vertex ( 0 ) ;
46+ if ( triangleVertex . IsAlmostEqualTo ( vertex ) ) return mesh . GetNormal ( i ) ;
47+ triangleVertex = triangle . get_Vertex ( 1 ) ;
48+ if ( triangleVertex . IsAlmostEqualTo ( vertex ) ) return mesh . GetNormal ( i ) ;
49+ triangleVertex = triangle . get_Vertex ( 2 ) ;
50+ if ( triangleVertex . IsAlmostEqualTo ( vertex ) ) return mesh . GetNormal ( i ) ;
51+ }
52+
53+ return XYZ . Zero ;
54+ case DistributionOfNormals . OnePerFace :
55+ return mesh . GetNormal ( 0 ) ;
56+ default :
57+ throw new ArgumentOutOfRangeException ( nameof ( normalDistribution ) , normalDistribution , null ) ;
58+ }
59+ }
60+
61+ public static List < XYZ > TessellateCircle ( XYZ center , XYZ normal , double radius )
62+ {
63+ var vertices = new List < XYZ > ( ) ;
64+ var segmentCount = InterpolateSegmentsCount ( radius ) ;
65+ var xDirection = normal . CrossProduct ( XYZ . BasisZ ) . Normalize ( ) * radius ;
66+ if ( xDirection . IsZeroLength ( ) )
67+ {
68+ xDirection = normal . CrossProduct ( XYZ . BasisX ) . Normalize ( ) * radius ;
69+ }
70+
71+ var yDirection = normal . CrossProduct ( xDirection ) . Normalize ( ) * radius ;
72+
73+ for ( var i = 0 ; i < segmentCount ; i ++ )
74+ {
75+ var angle = 2 * Math . PI * i / segmentCount ;
76+ var vertex = center + xDirection * Math . Cos ( angle ) + yDirection * Math . Sin ( angle ) ;
77+ vertices . Add ( vertex ) ;
78+ }
79+
80+ return vertices ;
81+ }
82+
83+ public static Solid ScaleSolid ( Solid solid , double scale )
84+ {
85+ if ( scale is 1 ) scale = EvaluateScale ( solid , Context . Application . VertexTolerance * 3 ) ;
86+
87+ var centroid = solid . GetBoundingBox ( ) . Transform . Origin ;
88+ var moveToCentroid = Transform . CreateTranslation ( - centroid ) ;
89+ var scaleTransform = Transform . Identity . ScaleBasis ( scale ) ;
90+ var moveBack = Transform . CreateTranslation ( centroid ) ;
91+ var combinedTransform = moveBack . Multiply ( scaleTransform ) . Multiply ( moveToCentroid ) ;
92+ return SolidUtils . CreateTransformed ( solid , combinedTransform ) ;
93+ }
94+
95+ public static int InterpolateSegmentsCount ( double diameter )
96+ {
97+ const int minSegments = 6 ;
98+ const int maxSegments = 33 ;
99+ const double minDiameter = 0.1 / 12d ;
100+ const double maxDiameter = 3 / 12d ;
101+
102+ if ( diameter <= minDiameter ) return minSegments ;
103+ if ( diameter >= maxDiameter ) return maxSegments ;
104+
105+ var normalDiameter = ( diameter - minDiameter ) / ( maxDiameter - minDiameter ) ;
106+ return ( int ) ( minSegments + normalDiameter * ( maxSegments - minSegments ) ) ;
107+ }
108+
109+ public static double InterpolateOffsetByDiameter ( double diameter )
110+ {
111+ const double minOffset = 0.01d ;
112+ const double maxOffset = 0.1d ;
113+ const double minDiameter = 0.1 / 12d ;
114+ const double maxDiameter = 3 / 12d ;
115+
116+ if ( diameter <= minDiameter ) return minOffset ;
117+ if ( diameter >= maxDiameter ) return maxOffset ;
118+
119+ var normalOffset = ( diameter - minDiameter ) / ( maxDiameter - minDiameter ) ;
120+ return minOffset + normalOffset * ( maxOffset - minOffset ) ;
121+ }
122+
123+ public static double InterpolateOffsetByArea ( double area )
124+ {
125+ const double minOffset = 0.01d ;
126+ const double maxOffset = 0.1d ;
127+ const double minArea = 0.01d ;
128+ const double maxArea = 1d ;
129+
130+ if ( area <= minArea ) return minOffset ;
131+ if ( area >= maxArea ) return maxOffset ;
132+
133+ var normalOffset = ( area - minArea ) / ( maxArea - minArea ) ;
134+ return minOffset + normalOffset * ( maxOffset - minOffset ) ;
135+ }
136+
137+ public static double InterpolateAxisLengthByArea ( double area )
138+ {
139+ const double minLength = 0.1d ;
140+ const double maxLength = 1d ;
141+ const double minArea = 0.01d ;
142+ const double maxArea = 1d ;
143+
144+ if ( area <= minArea ) return minLength ;
145+ if ( area >= maxArea ) return maxLength ;
146+
147+ var normalOffset = ( area - minArea ) / ( maxArea - minArea ) ;
148+ return minLength + normalOffset * ( maxLength - minLength ) ;
149+ }
150+
151+ public static double InterpolateAxisLengthByPoints ( XYZ min , XYZ max )
152+ {
153+ const double maxLength = 1d ;
154+
155+ var width = max . X - min . X ;
156+ var height = max . Y - min . Y ;
157+ var depth = max . Z - min . Z ;
158+
159+ var maxSize = Math . Max ( width , Math . Max ( height , depth ) ) ;
160+
161+ if ( maxLength * 2 < maxSize )
162+ {
163+ return maxLength ;
164+ }
165+
166+ return maxSize * 0.35 ;
167+ }
168+
169+ public static double ComputeMeshSurfaceArea ( Mesh mesh )
170+ {
171+ #if REVIT2024_OR_GREATER
172+ return mesh . ComputeSurfaceArea ( ) ;
173+ #else
174+ var surfaceArea = 0.0 ;
175+
176+ for ( var i = 0 ; i < mesh . NumTriangles ; i ++ )
177+ {
178+ var triangle = mesh . get_Triangle ( i ) ;
179+
180+ var vertex0 = triangle . get_Vertex ( 0 ) ;
181+ var vertex1 = triangle . get_Vertex ( 1 ) ;
182+ var vertex2 = triangle . get_Vertex ( 2 ) ;
183+
184+ var side1 = vertex1 - vertex0 ;
185+ var side2 = vertex2 - vertex0 ;
186+
187+ var crossProduct = side1 . CrossProduct ( side2 ) ;
188+
189+ surfaceArea += crossProduct . GetLength ( ) / 2.0 ;
190+ }
191+
192+ return surfaceArea ;
193+ #endif
194+ }
195+
196+ [ SuppressMessage ( "ReSharper" , "CompareOfFloatsByEqualityOperator" ) ]
197+ private static double EvaluateScale ( Solid solid , double offset )
198+ {
199+ var boundingBox = solid . GetBoundingBox ( ) ;
200+
201+ var currentLength = boundingBox . Max . X - boundingBox . Min . X ;
202+ var currentWidth = boundingBox . Max . Y - boundingBox . Min . Y ;
203+ var currentHeight = boundingBox . Max . Z - boundingBox . Min . Z ;
204+
205+ var maxDimension = Math . Max ( Math . Max ( currentLength , currentWidth ) , currentHeight ) ;
206+
207+ if ( maxDimension == currentLength ) return ( currentLength + offset ) / currentLength ;
208+ if ( maxDimension == currentWidth ) return ( currentWidth + offset ) / currentWidth ;
209+ return ( currentHeight + offset ) / currentHeight ;
210+ }
211+ }
0 commit comments