aboutsummaryrefslogtreecommitdiff
path: root/src/main/scala/sims/collision/Segment.scala
blob: 45bd7b9d067f84275d323c021c2f3f1bd4e1a37d (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
/*    _____ _ __  ________    ___                                      *\
**   / ___/(_)  |/  / ___/   |__ \  Simple Mechanics Simulator 2       **
**   \__ \/ / /|_/ /\__ \    __/ /  copyright (c) 2011 Jakob Odersky   **
**  ___/ / / /  / /___/ /   / __/                                      **
** /____/_/_/  /_//____/   /____/                                      **
\*                                                                     */

package sims.collision

import scala.math._
import sims.math._

/** A segment passing through two points.
  *  
  * @param point1 position vector of the first point
  * @param point2 position vector of the second point
  * @throws IllegalArgumentException if both vertices are equal */
case class Segment(point1: Vector2D, point2: Vector2D)
	extends Linear
	with Intersectable[Segment] {
	
  require(point1 != point2, "A segment must have two distinct vertices.")
  
  val point = point1
  
  /**Vector from <code>vertex1</code> to <code>vertex2</code>.*/
  val direction = point2 - point
  
  /**Length of this segment.*/
  val length = direction.length
  

  def closest(point: Vector2D) = {
  	var t = ((point - point1) dot (direction)) / (direction dot direction)
  	if (t < 0) t = 0
  	if (t > 1) t = 1
  	point1 + direction * t
    }
  
  def distance(p: Vector2D) = {
  	// For more concise code, the following substitutions are made:
  	// * point1 -> a
  	// * point2 -> b
  	// * p      -> c
  	
  	val ab = direction
  	val ac = p - point1
  	val bc = p - point2
  	
  	val e = ac dot ab
  	val distanceSquare = 
  		// Handle cases where c projects outside ab
  		if (e <= 0) ac dot ac
  		else if (e >= (ab dot ab)) bc dot bc
  		// Handle cases where c projects onto ab
  		else (ac dot ac) - e * e / (ab dot ab)
  	
  	math.sqrt(distanceSquare)
  }
 
  def clipped(reference: Segment): List[Vector2D] = {
		val clipped = Linear.clip(this.point1, this.point2, reference.point1, reference.direction)
		if (clipped.length == 0) Nil
		else Linear.clip(clipped(0), clipped(1), reference.point2, -reference.direction)
	}
	
  
  def intersection(segment: Segment): Option[Vector2D] = {
  	val n = segment.leftNormal
  	
  	// Handle case when two segments parallel
  	if ((n dot direction) == 0) None
  	else {
  		val t = (n dot (segment.point1 - point1)) / (n dot direction)
  		val i = point + direction * t
  		if (0 <= t && t <= 1 && (i - segment.point1).length <= segment.length) Some(i)
  		else None
  	}
	 /*
  	// Returns 2 times the signed triangle area. The result is positive if
  	// abc is ccw, negative if abc is cw, zero if abc is degenerate.
  	def signed2DTriArea(a: Vector2D, b: Vector2D, c: Vector2D) = {
  		(a.x - c.x) * (b.y - c.y) - (a.y - c.y) * (b.x - c.x);
  	}
  	
  	val a = point1; val b = point2; val c = segment.point; val d = segment.point2
	
    // Sign of areas correspond to which side of ab points c and d are
    val a1 = signed2DTriArea(a, b, d); // Compute winding of abd (+ or -)
    val a2 = signed2DTriArea(a, b, c); // To intersect, must have sign opposite of a1

    // If c and d are on different sides of ab, areas have different signs
    if (a1 * a2 < 0.0f) {
        // Compute signs for a and b with respect to segment cd
        val a3 = signed2DTriArea(c, d, a); // Compute winding of cda (+ or -)
        // Since area is constant a1-a2 = a3-a4, or a4=a3+a2-a1
        // float a4 = Signed2DTriArea(c, d, b); // Must have opposite sign of a3
        val a4 = a3 + a2 - a1;
        // Points a and b on different sides of cd if areas have different signs
        if (a3 * a4 < 0.0f) {
            // Segments intersect. Find intersection point along L(t)=a+t*(b-a).
            // Given height h1 of a over cd and height h2 of b over cd,
            // t = h1 / (h1 - h2) = (b*h1/2) / (b*h1/2 - b*h2/2) = a3 / (a3 - a4),
            // where b (the base of the triangles cda and cdb, i.e., the length
            // of cd) cancels out.
            val t = a3 / (a3 - a4);
            val p = a + (b - a) * t;
            return Some(p);
        }
    }
    // Segments not intersecting (or collinear)
    return None;
    */
  }
  
  
}