KiCad PCB EDA Suite
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 (C) 1992-2020 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, you may find one here:
19  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
20  * or you may search the http://www.gnu.org website for the version 2 license,
21  * or you may write to the Free Software Foundation, Inc.,
22  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
23  */
24 
29 #include "round_segment_3d.h"
30 #include "../shapes2D/round_segment_2d.h"
31 
32 
33 ROUND_SEGMENT::ROUND_SEGMENT( const ROUND_SEGMENT_2D& aSeg2D, float aZmin, float aZmax ) :
35  m_segment( aSeg2D.m_segment )
36 {
37  m_radius = aSeg2D.GetRadius();
39  m_inv_radius = 1.0f / m_radius;
40 
43 
44  m_bbox.Reset();
45 
47  SFVEC3F( m_segment.m_End.x, m_segment.m_End.y, aZmax ) );
48 
49  m_bbox.Set( m_bbox.Min() - SFVEC3F( m_radius, m_radius, 0.0f ),
50  m_bbox.Max() + SFVEC3F( m_radius, m_radius, 0.0f ) );
51 
54 
57 
59 }
60 
61 
62 bool ROUND_SEGMENT::Intersect( const RAY& aRay, HITINFO& aHitInfo ) const
63 {
64  // Top / Bottom plane
65  float zPlanePos = aRay.m_dirIsNeg[2]? m_bbox.Max().z : m_bbox.Min().z;
66 
67  float tPlane = ( zPlanePos - aRay.m_Origin.z ) * aRay.m_InvDir.z;
68 
69  if( ( tPlane >= aHitInfo.m_tHit ) || ( tPlane < FLT_EPSILON ) )
70  return false; // Early exit
71 
72  SFVEC2F planeHitPoint2d( aRay.m_Origin.x + aRay.m_Dir.x * tPlane,
73  aRay.m_Origin.y + aRay.m_Dir.y * tPlane );
74 
75  float dSquared = m_segment.DistanceToPointSquared( planeHitPoint2d );
76 
77  if( dSquared <= m_radius_squared )
78  {
79  if( tPlane < aHitInfo.m_tHit )
80  {
81  aHitInfo.m_tHit = tPlane;
82  aHitInfo.m_HitPoint = SFVEC3F( planeHitPoint2d.x, planeHitPoint2d.y,
83  aRay.m_Origin.z + aRay.m_Dir.z * tPlane );
84  aHitInfo.m_HitNormal = SFVEC3F( 0.0f, 0.0f, aRay.m_dirIsNeg[2] ? 1.0f : -1.0f );
85  aHitInfo.pHitObject = this;
86 
87  m_material->Generate( aHitInfo.m_HitNormal, aRay, aHitInfo );
88 
89  return true;
90  }
91 
92  return false;
93  }
94 
95  // Test LEFT / RIGHT plane
96  float normal_dot_ray = glm::dot( m_plane_dir_right, aRay.m_Dir );
97 
98  if( normal_dot_ray < 0.0f ) // If the dot is neg, the it hits the plane
99  {
100  const float n_dot_ray_origin = glm::dot( m_plane_dir_right,
101  m_center_right - aRay.m_Origin );
102  const float t = n_dot_ray_origin / normal_dot_ray;
103 
104  if( t > 0.0f )
105  {
106  const SFVEC3F hitP = aRay.at( t );
107 
108  const SFVEC3F v = hitP - m_center_right;
109  const float len = glm::dot( v, v );
110 
111  if( ( len <= m_seglen_over_two_squared ) && ( hitP.z >= m_bbox.Min().z )
112  && ( hitP.z <= m_bbox.Max().z ) )
113  {
114  if( t < aHitInfo.m_tHit )
115  {
116  aHitInfo.m_tHit = t;
117  aHitInfo.m_HitPoint = hitP;
119  0.0f );
120  aHitInfo.pHitObject = this;
121 
122  m_material->Generate( aHitInfo.m_HitNormal, aRay, aHitInfo );
123 
124  return true;
125  }
126 
127  return false;
128  }
129  }
130  }
131  else
132  {
133  normal_dot_ray = glm::dot( m_plane_dir_left, aRay.m_Dir );
134 
135  if( normal_dot_ray < 0.0f ) // If the dot is neg, the it hits the plane
136  {
137  const float n_dot_ray_origin = glm::dot( m_plane_dir_left,
138  m_center_left - aRay.m_Origin );
139  const float t = n_dot_ray_origin / normal_dot_ray;
140 
141  if( t > 0.0f )
142  {
143  const SFVEC3F hitP = aRay.at( t );
144 
145  const SFVEC3F v = hitP - m_center_left;
146  const float len = glm::dot( v, v );
147 
148  if( ( len <= m_seglen_over_two_squared ) && ( hitP.z >= m_bbox.Min().z )
149  && ( hitP.z <= m_bbox.Max().z ) )
150  {
151  if( t < aHitInfo.m_tHit )
152  {
153  aHitInfo.m_tHit = t;
154  aHitInfo.m_HitPoint = hitP;
156  0.0f );
157  aHitInfo.pHitObject = this;
158 
159  m_material->Generate( aHitInfo.m_HitNormal, aRay, aHitInfo );
160 
161  return true;
162  }
163 
164  return false;
165  }
166  }
167  }
168  }
169 
170  // Based on: http://www.cs.utah.edu/~lha/Code%206620%20/Ray4/Cylinder.cpp
171  // Ray-sphere intersection: geometric
172  const double OCx_Start = aRay.m_Origin.x - m_segment.m_Start.x;
173  const double OCy_Start = aRay.m_Origin.y - m_segment.m_Start.y;
174 
175  const double p_dot_p_Start = OCx_Start * OCx_Start + OCy_Start * OCy_Start;
176 
177  const double a = (double)aRay.m_Dir.x * (double)aRay.m_Dir.x +
178  (double)aRay.m_Dir.y * (double)aRay.m_Dir.y;
179 
180  const double b_Start = (double)aRay.m_Dir.x * (double)OCx_Start +
181  (double)aRay.m_Dir.y * (double)OCy_Start;
182 
183  const double c_Start = p_dot_p_Start - m_radius_squared;
184 
185  const float delta_Start = (float) ( b_Start * b_Start - a * c_Start );
186 
187  if( delta_Start > FLT_EPSILON )
188  {
189  const float sdelta = sqrtf( delta_Start );
190  const float t = ( -b_Start - sdelta ) / a;
191  const float z = aRay.m_Origin.z + t * aRay.m_Dir.z;
192 
193  if( ( z >= m_bbox.Min().z ) && ( z <= m_bbox.Max().z ) )
194  {
195  if( t < aHitInfo.m_tHit )
196  {
197  aHitInfo.m_tHit = t;
198  aHitInfo.m_HitPoint = aRay.at( t );
199 
200  const SFVEC2F hitPoint2D = SFVEC2F( aHitInfo.m_HitPoint.x, aHitInfo.m_HitPoint.y );
201 
202  aHitInfo.m_HitNormal =
203  SFVEC3F( ( hitPoint2D.x - m_segment.m_Start.x ) * m_inv_radius,
204  ( hitPoint2D.y - m_segment.m_Start.y ) * m_inv_radius, 0.0f );
205 
206  aHitInfo.pHitObject = this;
207 
208  m_material->Generate( aHitInfo.m_HitNormal, aRay, aHitInfo );
209 
210  return true;
211  }
212 
213  return false;
214  }
215  }
216 
217  const double OCx_End = aRay.m_Origin.x - m_segment.m_End.x;
218  const double OCy_End = aRay.m_Origin.y - m_segment.m_End.y;
219 
220  const double p_dot_p_End = OCx_End * OCx_End + OCy_End * OCy_End;
221 
222  const double b_End = (double)aRay.m_Dir.x * (double)OCx_End +
223  (double)aRay.m_Dir.y * (double)OCy_End;
224 
225  const double c_End = p_dot_p_End - m_radius_squared;
226 
227  const float delta_End = (float)(b_End * b_End - a * c_End);
228 
229  if( delta_End > FLT_EPSILON )
230  {
231  const float sdelta = sqrtf( delta_End );
232  const float t = ( -b_End - sdelta ) / a;
233  const float z = aRay.m_Origin.z + t * aRay.m_Dir.z;
234 
235  if( ( z >= m_bbox.Min().z ) && ( z <= m_bbox.Max().z ) )
236  {
237  if( t < aHitInfo.m_tHit )
238  {
239  aHitInfo.m_tHit = t;
240  aHitInfo.m_HitPoint = aRay.at( t );
241 
242  const SFVEC2F hitPoint2D = SFVEC2F( aHitInfo.m_HitPoint.x, aHitInfo.m_HitPoint.y );
243 
244  aHitInfo.m_HitNormal =
245  SFVEC3F( ( hitPoint2D.x - m_segment.m_End.x ) * m_inv_radius,
246  ( hitPoint2D.y - m_segment.m_End.y ) * m_inv_radius, 0.0f );
247  aHitInfo.pHitObject = this;
248 
249  m_material->Generate( aHitInfo.m_HitNormal, aRay, aHitInfo );
250 
251  return true;
252  }
253 
254  return false;
255  }
256  }
257 
258  return false;
259 }
260 
261 
262 bool ROUND_SEGMENT::IntersectP( const RAY& aRay, float aMaxDistance ) const
263 {
264  // Top / Bottom plane
265  const float zPlanePos = aRay.m_dirIsNeg[2]? m_bbox.Max().z : m_bbox.Min().z;
266 
267  const float tPlane = ( zPlanePos - aRay.m_Origin.z ) * aRay.m_InvDir.z;
268 
269  if( ( tPlane >= aMaxDistance) || ( tPlane < FLT_EPSILON ) )
270  return false; // Early exit
271 
272  const SFVEC2F planeHitPoint2d( aRay.m_Origin.x + aRay.m_Dir.x * tPlane,
273  aRay.m_Origin.y + aRay.m_Dir.y * tPlane );
274 
275  const float dSquared = m_segment.DistanceToPointSquared( planeHitPoint2d );
276 
277  if( dSquared <= m_radius_squared )
278  {
279  if( tPlane < aMaxDistance )
280  return true;
281 
282  return false;
283  }
284 
285  // Since the IntersectP is used for shadows, we are simplifying the test
286  // intersection and only consider the top/bottom plane of the segment
287  return false;
288 
290 #if 0
291  // Test LEFT / RIGHT plane
292  float normal_dot_ray = glm::dot( m_plane_dir_right, aRay.m_Dir );
293 
294  if( normal_dot_ray < 0.0f ) // If the dot is neg, the it hits the plane
295  {
296  float n_dot_ray_origin = glm::dot( m_plane_dir_right, m_center_right - aRay.m_Origin );
297  float t = n_dot_ray_origin / normal_dot_ray;
298 
299  if( t > 0.0f )
300  {
301  SFVEC3F hitP = aRay.at( t );
302 
303  SFVEC3F v = hitP - m_center_right;
304  float len = glm::dot( v, v );
305 
306  if( ( len <= m_seglen_over_two_squared ) &&
307  ( hitP.z >= m_bbox.Min().z ) && ( hitP.z <= m_bbox.Max().z ) )
308  {
309  if( t < aMaxDistance )
310  return true;
311 
312  return false;
313  }
314  }
315  }
316  else
317  {
318  normal_dot_ray = glm::dot( m_plane_dir_left, aRay.m_Dir );
319 
320  if( normal_dot_ray < 0.0f ) // If the dot is neg, the it hits the plane
321  {
322  const float n_dot_ray_origin = glm::dot( m_plane_dir_left,
323  m_center_left - aRay.m_Origin );
324  const float t = n_dot_ray_origin / normal_dot_ray;
325 
326  if( t > 0.0f )
327  {
328  SFVEC3F hitP = aRay.at( t );
329 
330  SFVEC3F v = hitP - m_center_left;
331  float len = glm::dot( v, v );
332 
333  if( ( len <= m_seglen_over_two_squared ) &&
334  ( hitP.z >= m_bbox.Min().z ) && ( hitP.z <= m_bbox.Max().z ) )
335  {
336  if( t < aMaxDistance )
337  return true;
338 
339  return false;
340  }
341  }
342  }
343  }
344 
345  // Based on: http://www.cs.utah.edu/~lha/Code%206620%20/Ray4/Cylinder.cpp
346  // Ray-sphere intersection: geometric
347 
348  double OCx_Start = aRay.m_Origin.x - m_segment.m_Start.x;
349  double OCy_Start = aRay.m_Origin.y - m_segment.m_Start.y;
350 
351  double p_dot_p_Start = OCx_Start * OCx_Start + OCy_Start * OCy_Start;
352 
353  double a = (double)aRay.m_Dir.x * (double)aRay.m_Dir.x +
354  (double)aRay.m_Dir.y * (double)aRay.m_Dir.y;
355 
356  double b_Start = (double)aRay.m_Dir.x * (double)OCx_Start +
357  (double)aRay.m_Dir.y * (double)OCy_Start;
358 
359  double c_Start = p_dot_p_Start - m_radius_squared;
360 
361  float delta_Start = (float)(b_Start * b_Start - a * c_Start);
362 
363  if( delta_Start > FLT_EPSILON )
364  {
365  float sdelta = sqrtf( delta_Start );
366  float t = (-b_Start - sdelta) / a;
367  float z = aRay.m_Origin.z + t * aRay.m_Dir.z;
368 
369  if( ( z >= m_bbox.Min().z ) && ( z <= m_bbox.Max().z ) )
370  {
371  if( t < aMaxDistance )
372  return true;
373 
374  return false;
375  }
376  }
377 
378  double OCx_End = aRay.m_Origin.x - m_segment.m_End.x;
379  double OCy_End = aRay.m_Origin.y - m_segment.m_End.y;
380 
381  double p_dot_p_End = OCx_End * OCx_End + OCy_End * OCy_End;
382 
383 
384  double b_End = (double)aRay.m_Dir.x * (double)OCx_End +
385  (double)aRay.m_Dir.y * (double)OCy_End;
386 
387  double c_End = p_dot_p_End - m_radius_squared;
388 
389  float delta_End = (float)(b_End * b_End - a * c_End);
390 
391  if( delta_End > FLT_EPSILON )
392  {
393  float sdelta = sqrtf( delta_End );
394  float t = ( -b_End - sdelta ) / a;
395  float z = aRay.m_Origin.z + t * aRay.m_Dir.z;
396 
397  if( ( z >= m_bbox.Min().z ) && ( z <= m_bbox.Max().z ) )
398  {
399  if( t < aMaxDistance )
400  return true;
401 
402  return false;
403  }
404  }
405 
406  return false;
407 #endif
408 }
409 
410 
411 bool ROUND_SEGMENT::Intersects( const BBOX_3D& aBBox ) const
412 {
414  return m_bbox.Intersects( aBBox );
415 }
416 
417 
418 SFVEC3F ROUND_SEGMENT::GetDiffuseColor( const HITINFO& /* aHitInfo */ ) const
419 {
420  return m_diffusecolor;
421 }
SFVEC3F m_plane_dir_left
const SFVEC3F & Max() const
Return the maximum vertex pointer.
Definition: bbox_3d.h:198
float GetRadius() const
Manage a bounding box defined by two SFVEC3F min max points.
Definition: bbox_3d.h:41
void Generate(SFVEC3F &aNormal, const RAY &aRay, const HITINFO &aHitInfo) const
Definition: material.cpp:89
RAYSEG2D m_segment
SFVEC3F m_center_left
SFVEC2F m_Dir
Definition: ray.h:110
Definition: ray.h:62
float m_tHit
( 4) distance
Definition: hitinfo.h:38
SFVEC3F at(float t) const
Definition: ray.h:84
bool Intersects(const BBOX_3D &aBBox) const override
const SFVEC3F & Min() const
Return the minimum vertex pointer.
Definition: bbox_3d.h:191
SFVEC3F m_InvDir
Definition: ray.h:70
SFVEC3F m_HitPoint
(12) hit position
Definition: hitinfo.h:44
SFVEC3F GetCenter() const
Return the center point of the bounding box.
Definition: bbox_3d.cpp:132
glm::vec2 SFVEC2F
Definition: xv3d_types.h:42
BBOX_3D m_bbox
Definition: object_3d.h:97
unsigned int m_dirIsNeg[3]
Definition: ray.h:75
float m_Length
Definition: ray.h:112
float m_seglen_over_two_squared
SFVEC2F m_End
Definition: ray.h:108
const MATERIAL * m_material
Definition: object_3d.h:100
const OBJECT_3D * pHitObject
( 4) Object that was hitted
Definition: hitinfo.h:40
SFVEC3F m_Dir
Definition: ray.h:67
SFVEC3F GetDiffuseColor(const HITINFO &aHitInfo) const override
ROUND_SEGMENT(const ROUND_SEGMENT_2D &aSeg2D, float aZmin, float aZmax)
void Set(const SFVEC3F &aPbMin, const SFVEC3F &aPbMax)
Set bounding box with new parameters.
Definition: bbox_3d.cpp:68
SFVEC3F m_Origin
Definition: ray.h:64
SFVEC2F m_Start
Definition: ray.h:107
void ScaleNextUp()
Scale a bounding box to the next float representation making it larger.
Definition: bbox_3d.cpp:194
SFVEC3F m_centroid
Definition: object_3d.h:98
Stores the hit information of a ray with a point on the surface of a object.
Definition: hitinfo.h:35
glm::vec3 SFVEC3F
Definition: xv3d_types.h:44
float DistanceToPointSquared(const SFVEC2F &aPoint) const
Definition: ray.cpp:294
bool IntersectP(const RAY &aRay, float aMaxDistance) const override
SFVEC3F m_HitNormal
(12) normal at the hit point
Definition: hitinfo.h:37
OBJECT_3D_TYPE
Definition: object_3d.h:38
SFVEC3F m_plane_dir_right
bool Intersect(const RAY &aRay, HITINFO &aHitInfo) const override
SFVEC3F m_center_right
void Reset()
Reset the bounding box to zero and de-initialize it.
Definition: bbox_3d.cpp:95
bool Intersects(const BBOX_3D &aBBox) const
Test if a bounding box intersects this box.
Definition: bbox_3d.cpp:218
SFVEC3F m_diffusecolor