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