KiCad PCB EDA Suite
Loading...
Searching...
No Matches
round_segment_3d.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright (C) 2015-2016 Mario Luzeiro <[email protected]>
5 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <https://www.gnu.org/licenses/>.
19 */
20
24
25#include "round_segment_3d.h"
27
28
29ROUND_SEGMENT::ROUND_SEGMENT( const ROUND_SEGMENT_2D& aSeg2D, float aZmin, float aZmax ) :
31 m_segment( aSeg2D.m_segment )
32{
33 m_radius = aSeg2D.GetRadius();
35 m_inv_radius = 1.0f / m_radius;
36
37 m_plane_dir_left = SFVEC3F( -m_segment.m_Dir.y, m_segment.m_Dir.x, 0.0f );
38 m_plane_dir_right = SFVEC3F( m_segment.m_Dir.y, -m_segment.m_Dir.x, 0.0f );
39
40 m_bbox.Reset();
41
42 m_bbox.Set( SFVEC3F( m_segment.m_Start.x, m_segment.m_Start.y, aZmin ),
43 SFVEC3F( m_segment.m_End.x, m_segment.m_End.y, aZmax ) );
44
45 m_bbox.Set( m_bbox.Min() - SFVEC3F( m_radius, m_radius, 0.0f ),
46 m_bbox.Max() + SFVEC3F( m_radius, m_radius, 0.0f ) );
47
48 m_bbox.ScaleNextUp();
49 m_centroid = m_bbox.GetCenter();
50
53
54 m_seglen_over_two_squared = ( m_segment.m_Length / 2.0f ) * ( m_segment.m_Length / 2.0f );
55}
56
57
58bool ROUND_SEGMENT::Intersect( const RAY& aRay, HITINFO& aHitInfo ) const
59{
60 // Top / Bottom plane
61 float zPlanePos = aRay.m_dirIsNeg[2]? m_bbox.Max().z : m_bbox.Min().z;
62
63 float tPlane = ( zPlanePos - aRay.m_Origin.z ) * aRay.m_InvDir.z;
64
65 if( ( tPlane >= aHitInfo.m_tHit ) || ( tPlane < FLT_EPSILON ) )
66 return false; // Early exit
67
68 SFVEC2F planeHitPoint2d( aRay.m_Origin.x + aRay.m_Dir.x * tPlane,
69 aRay.m_Origin.y + aRay.m_Dir.y * tPlane );
70
71 float dSquared = m_segment.DistanceToPointSquared( planeHitPoint2d );
72
73 if( dSquared <= m_radius_squared )
74 {
75 if( tPlane < aHitInfo.m_tHit )
76 {
77 aHitInfo.m_tHit = tPlane;
78 aHitInfo.m_HitPoint = SFVEC3F( planeHitPoint2d.x, planeHitPoint2d.y,
79 aRay.m_Origin.z + aRay.m_Dir.z * tPlane );
80 aHitInfo.m_HitNormal = SFVEC3F( 0.0f, 0.0f, aRay.m_dirIsNeg[2] ? 1.0f : -1.0f );
81 aHitInfo.pHitObject = this;
82
83 m_material->Generate( aHitInfo.m_HitNormal, aRay, aHitInfo );
84
85 return true;
86 }
87
88 return false;
89 }
90
91 // Test LEFT / RIGHT plane
92 float normal_dot_ray = glm::dot( m_plane_dir_right, aRay.m_Dir );
93
94 if( normal_dot_ray < 0.0f ) // If the dot is neg, the it hits the plane
95 {
96 const float n_dot_ray_origin = glm::dot( m_plane_dir_right,
97 m_center_right - aRay.m_Origin );
98 const float t = n_dot_ray_origin / normal_dot_ray;
99
100 if( t > 0.0f )
101 {
102 const SFVEC3F hitP = aRay.at( t );
103
104 const SFVEC3F v = hitP - m_center_right;
105 const float len = glm::dot( v, v );
106
107 if( ( len <= m_seglen_over_two_squared ) && ( hitP.z >= m_bbox.Min().z )
108 && ( hitP.z <= m_bbox.Max().z ) )
109 {
110 if( t < aHitInfo.m_tHit )
111 {
112 aHitInfo.m_tHit = t;
113 aHitInfo.m_HitPoint = hitP;
115 0.0f );
116 aHitInfo.pHitObject = this;
117
118 m_material->Generate( aHitInfo.m_HitNormal, aRay, aHitInfo );
119
120 return true;
121 }
122
123 return false;
124 }
125 }
126 }
127 else
128 {
129 normal_dot_ray = glm::dot( m_plane_dir_left, aRay.m_Dir );
130
131 if( normal_dot_ray < 0.0f ) // If the dot is neg, the it hits the plane
132 {
133 const float n_dot_ray_origin = glm::dot( m_plane_dir_left,
134 m_center_left - aRay.m_Origin );
135 const float t = n_dot_ray_origin / normal_dot_ray;
136
137 if( t > 0.0f )
138 {
139 const SFVEC3F hitP = aRay.at( t );
140
141 const SFVEC3F v = hitP - m_center_left;
142 const float len = glm::dot( v, v );
143
144 if( ( len <= m_seglen_over_two_squared ) && ( hitP.z >= m_bbox.Min().z )
145 && ( hitP.z <= m_bbox.Max().z ) )
146 {
147 if( t < aHitInfo.m_tHit )
148 {
149 aHitInfo.m_tHit = t;
150 aHitInfo.m_HitPoint = hitP;
152 0.0f );
153 aHitInfo.pHitObject = this;
154
155 m_material->Generate( aHitInfo.m_HitNormal, aRay, aHitInfo );
156
157 return true;
158 }
159
160 return false;
161 }
162 }
163 }
164 }
165
166 // Based on: http://www.cs.utah.edu/~lha/Code%206620%20/Ray4/Cylinder.cpp
167 // Ray-sphere intersection: geometric
168 const double OCx_Start = aRay.m_Origin.x - m_segment.m_Start.x;
169 const double OCy_Start = aRay.m_Origin.y - m_segment.m_Start.y;
170
171 const double p_dot_p_Start = OCx_Start * OCx_Start + OCy_Start * OCy_Start;
172
173 const double a = (double)aRay.m_Dir.x * (double)aRay.m_Dir.x +
174 (double)aRay.m_Dir.y * (double)aRay.m_Dir.y;
175
176 const double b_Start = (double)aRay.m_Dir.x * (double)OCx_Start +
177 (double)aRay.m_Dir.y * (double)OCy_Start;
178
179 const double c_Start = p_dot_p_Start - m_radius_squared;
180
181 const float delta_Start = (float) ( b_Start * b_Start - a * c_Start );
182
183 if( delta_Start > FLT_EPSILON )
184 {
185 const float sdelta = sqrtf( delta_Start );
186 const float t = ( -b_Start - sdelta ) / a;
187 const float z = aRay.m_Origin.z + t * aRay.m_Dir.z;
188
189 if( ( z >= m_bbox.Min().z ) && ( z <= m_bbox.Max().z ) )
190 {
191 if( t < aHitInfo.m_tHit )
192 {
193 aHitInfo.m_tHit = t;
194 aHitInfo.m_HitPoint = aRay.at( t );
195
196 const SFVEC2F hitPoint2D = SFVEC2F( aHitInfo.m_HitPoint.x, aHitInfo.m_HitPoint.y );
197
198 aHitInfo.m_HitNormal =
199 SFVEC3F( ( hitPoint2D.x - m_segment.m_Start.x ) * m_inv_radius,
200 ( hitPoint2D.y - m_segment.m_Start.y ) * m_inv_radius, 0.0f );
201
202 aHitInfo.pHitObject = this;
203
204 m_material->Generate( aHitInfo.m_HitNormal, aRay, aHitInfo );
205
206 return true;
207 }
208
209 return false;
210 }
211 }
212
213 const double OCx_End = aRay.m_Origin.x - m_segment.m_End.x;
214 const double OCy_End = aRay.m_Origin.y - m_segment.m_End.y;
215
216 const double p_dot_p_End = OCx_End * OCx_End + OCy_End * OCy_End;
217
218 const double b_End = (double)aRay.m_Dir.x * (double)OCx_End +
219 (double)aRay.m_Dir.y * (double)OCy_End;
220
221 const double c_End = p_dot_p_End - m_radius_squared;
222
223 const float delta_End = (float)(b_End * b_End - a * c_End);
224
225 if( delta_End > FLT_EPSILON )
226 {
227 const float sdelta = sqrtf( delta_End );
228 const float t = ( -b_End - sdelta ) / a;
229 const float z = aRay.m_Origin.z + t * aRay.m_Dir.z;
230
231 if( ( z >= m_bbox.Min().z ) && ( z <= m_bbox.Max().z ) )
232 {
233 if( t < aHitInfo.m_tHit )
234 {
235 aHitInfo.m_tHit = t;
236 aHitInfo.m_HitPoint = aRay.at( t );
237
238 const SFVEC2F hitPoint2D = SFVEC2F( aHitInfo.m_HitPoint.x, aHitInfo.m_HitPoint.y );
239
240 aHitInfo.m_HitNormal =
241 SFVEC3F( ( hitPoint2D.x - m_segment.m_End.x ) * m_inv_radius,
242 ( hitPoint2D.y - m_segment.m_End.y ) * m_inv_radius, 0.0f );
243 aHitInfo.pHitObject = this;
244
245 m_material->Generate( aHitInfo.m_HitNormal, aRay, aHitInfo );
246
247 return true;
248 }
249
250 return false;
251 }
252 }
253
254 return false;
255}
256
257
258bool ROUND_SEGMENT::IntersectP( const RAY& aRay, float aMaxDistance ) const
259{
260 // Top / Bottom plane
261 const float zPlanePos = aRay.m_dirIsNeg[2]? m_bbox.Max().z : m_bbox.Min().z;
262
263 const float tPlane = ( zPlanePos - aRay.m_Origin.z ) * aRay.m_InvDir.z;
264
265 if( ( tPlane >= aMaxDistance) || ( tPlane < FLT_EPSILON ) )
266 return false; // Early exit
267
268 const SFVEC2F planeHitPoint2d( aRay.m_Origin.x + aRay.m_Dir.x * tPlane,
269 aRay.m_Origin.y + aRay.m_Dir.y * tPlane );
270
271 const float dSquared = m_segment.DistanceToPointSquared( planeHitPoint2d );
272
273 if( dSquared <= m_radius_squared )
274 {
275 if( tPlane < aMaxDistance )
276 return true;
277
278 return false;
279 }
280
281 // Since the IntersectP is used for shadows, we are simplifying the test
282 // intersection and only consider the top/bottom plane of the segment
283 return false;
284
286#if 0
287 // Test LEFT / RIGHT plane
288 float normal_dot_ray = glm::dot( m_plane_dir_right, aRay.m_Dir );
289
290 if( normal_dot_ray < 0.0f ) // If the dot is neg, the it hits the plane
291 {
292 float n_dot_ray_origin = glm::dot( m_plane_dir_right, m_center_right - aRay.m_Origin );
293 float t = n_dot_ray_origin / normal_dot_ray;
294
295 if( t > 0.0f )
296 {
297 SFVEC3F hitP = aRay.at( t );
298
299 SFVEC3F v = hitP - m_center_right;
300 float len = glm::dot( v, v );
301
302 if( ( len <= m_seglen_over_two_squared ) &&
303 ( hitP.z >= m_bbox.Min().z ) && ( hitP.z <= m_bbox.Max().z ) )
304 {
305 if( t < aMaxDistance )
306 return true;
307
308 return false;
309 }
310 }
311 }
312 else
313 {
314 normal_dot_ray = glm::dot( m_plane_dir_left, aRay.m_Dir );
315
316 if( normal_dot_ray < 0.0f ) // If the dot is neg, the it hits the plane
317 {
318 const float n_dot_ray_origin = glm::dot( m_plane_dir_left,
319 m_center_left - aRay.m_Origin );
320 const float t = n_dot_ray_origin / normal_dot_ray;
321
322 if( t > 0.0f )
323 {
324 SFVEC3F hitP = aRay.at( t );
325
326 SFVEC3F v = hitP - m_center_left;
327 float len = glm::dot( v, v );
328
329 if( ( len <= m_seglen_over_two_squared ) &&
330 ( hitP.z >= m_bbox.Min().z ) && ( hitP.z <= m_bbox.Max().z ) )
331 {
332 if( t < aMaxDistance )
333 return true;
334
335 return false;
336 }
337 }
338 }
339 }
340
341 // Based on: http://www.cs.utah.edu/~lha/Code%206620%20/Ray4/Cylinder.cpp
342 // Ray-sphere intersection: geometric
343
344 double OCx_Start = aRay.m_Origin.x - m_segment.m_Start.x;
345 double OCy_Start = aRay.m_Origin.y - m_segment.m_Start.y;
346
347 double p_dot_p_Start = OCx_Start * OCx_Start + OCy_Start * OCy_Start;
348
349 double a = (double)aRay.m_Dir.x * (double)aRay.m_Dir.x +
350 (double)aRay.m_Dir.y * (double)aRay.m_Dir.y;
351
352 double b_Start = (double)aRay.m_Dir.x * (double)OCx_Start +
353 (double)aRay.m_Dir.y * (double)OCy_Start;
354
355 double c_Start = p_dot_p_Start - m_radius_squared;
356
357 float delta_Start = (float)( b_Start * b_Start - a * c_Start );
358
359 if( delta_Start > FLT_EPSILON )
360 {
361 float sdelta = sqrtf( delta_Start );
362 float t = ( -b_Start - sdelta ) / a;
363 float z = aRay.m_Origin.z + t * aRay.m_Dir.z;
364
365 if( ( z >= m_bbox.Min().z ) && ( z <= m_bbox.Max().z ) )
366 {
367 if( t < aMaxDistance )
368 return true;
369
370 return false;
371 }
372 }
373
374 double OCx_End = aRay.m_Origin.x - m_segment.m_End.x;
375 double OCy_End = aRay.m_Origin.y - m_segment.m_End.y;
376
377 double p_dot_p_End = OCx_End * OCx_End + OCy_End * OCy_End;
378
379
380 double b_End = (double)aRay.m_Dir.x * (double)OCx_End +
381 (double)aRay.m_Dir.y * (double)OCy_End;
382
383 double c_End = p_dot_p_End - m_radius_squared;
384
385 float delta_End = (float)(b_End * b_End - a * c_End);
386
387 if( delta_End > FLT_EPSILON )
388 {
389 float sdelta = sqrtf( delta_End );
390 float t = ( -b_End - sdelta ) / a;
391 float z = aRay.m_Origin.z + t * aRay.m_Dir.z;
392
393 if( ( z >= m_bbox.Min().z ) && ( z <= m_bbox.Max().z ) )
394 {
395 if( t < aMaxDistance )
396 return true;
397
398 return false;
399 }
400 }
401
402 return false;
403#endif
404}
405
406
407bool ROUND_SEGMENT::Intersects( const BBOX_3D& aBBox ) const
408{
410 return m_bbox.Intersects( aBBox );
411}
412
413
414SFVEC3F ROUND_SEGMENT::GetDiffuseColor( const HITINFO& /* aHitInfo */ ) const
415{
416 return m_diffusecolor;
417}
BBOX_3D m_bbox
Definition object_3d.h:93
SFVEC3F m_centroid
Definition object_3d.h:94
OBJECT_3D(OBJECT_3D_TYPE aObjType)
Definition object_3d.cpp:36
const MATERIAL * m_material
Definition object_3d.h:96
float GetRadius() const
bool Intersects(const BBOX_3D &aBBox) const override
float m_seglen_over_two_squared
SFVEC3F m_plane_dir_right
SFVEC3F GetDiffuseColor(const HITINFO &aHitInfo) const override
bool Intersect(const RAY &aRay, HITINFO &aHitInfo) const override
bool IntersectP(const RAY &aRay, float aMaxDistance) const override
ROUND_SEGMENT(const ROUND_SEGMENT_2D &aSeg2D, float aZmin, float aZmax)
SFVEC3F m_plane_dir_left
OBJECT_3D_TYPE
Definition object_3d.h:35
Manage a bounding box defined by two SFVEC3F min max points.
Definition bbox_3d.h:39
Stores the hit information of a ray with a point on the surface of a object.
Definition hitinfo.h:32
float m_tHit
( 4) distance
Definition hitinfo.h:34
const OBJECT_3D * pHitObject
( 4) Object that was hitted
Definition hitinfo.h:36
SFVEC3F m_HitNormal
(12) normal at the hit point
Definition hitinfo.h:33
SFVEC3F m_HitPoint
(12) hit position
Definition hitinfo.h:40
Definition ray.h:59
SFVEC3F m_Dir
Definition ray.h:63
unsigned int m_dirIsNeg[3]
Definition ray.h:71
SFVEC3F m_InvDir
Definition ray.h:66
SFVEC3F m_Origin
Definition ray.h:60
SFVEC3F at(float t) const
Definition ray.h:80
glm::vec2 SFVEC2F
Definition xv3d_types.h:38
glm::vec3 SFVEC3F
Definition xv3d_types.h:40