diff --git a/.classpath b/.classpath
new file mode 100644
index 0000000..cd29d98
--- /dev/null
+++ b/.classpath
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/.gitignore b/.gitignore
index 32858aa..0ed9f38 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,9 +4,10 @@
.mtj.tmp/
# Package Files #
-*.jar
*.war
*.ear
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
+/build/
+*.settings
diff --git a/.project b/.project
new file mode 100644
index 0000000..89a000d
--- /dev/null
+++ b/.project
@@ -0,0 +1,31 @@
+
+
+ Knot-Renderer-Servlet
+
+
+
+
+
+ org.eclipse.jdt.core.javabuilder
+
+
+
+
+ org.eclipse.wst.common.project.facet.core.builder
+
+
+
+
+ org.eclipse.wst.validation.validationbuilder
+
+
+
+
+
+ org.eclipse.jem.workbench.JavaEMFNature
+ org.eclipse.wst.common.modulecore.ModuleCoreNature
+ org.eclipse.wst.common.project.facet.core.nature
+ org.eclipse.jdt.core.javanature
+ org.eclipse.wst.jsdt.core.jsNature
+
+
diff --git a/.settings/.jsdtscope b/.settings/.jsdtscope
new file mode 100644
index 0000000..92e666d
--- /dev/null
+++ b/.settings/.jsdtscope
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..0c68a61
--- /dev/null
+++ b/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,7 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
+org.eclipse.jdt.core.compiler.compliance=1.8
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.source=1.8
diff --git a/.settings/org.eclipse.wst.common.component b/.settings/org.eclipse.wst.common.component
new file mode 100644
index 0000000..ee26233
--- /dev/null
+++ b/.settings/org.eclipse.wst.common.component
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/.settings/org.eclipse.wst.common.project.facet.core.xml b/.settings/org.eclipse.wst.common.project.facet.core.xml
new file mode 100644
index 0000000..bf8f8be
--- /dev/null
+++ b/.settings/org.eclipse.wst.common.project.facet.core.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/.settings/org.eclipse.wst.jsdt.ui.superType.container b/.settings/org.eclipse.wst.jsdt.ui.superType.container
new file mode 100644
index 0000000..3bd5d0a
--- /dev/null
+++ b/.settings/org.eclipse.wst.jsdt.ui.superType.container
@@ -0,0 +1 @@
+org.eclipse.wst.jsdt.launching.baseBrowserLibrary
\ No newline at end of file
diff --git a/.settings/org.eclipse.wst.jsdt.ui.superType.name b/.settings/org.eclipse.wst.jsdt.ui.superType.name
new file mode 100644
index 0000000..05bd71b
--- /dev/null
+++ b/.settings/org.eclipse.wst.jsdt.ui.superType.name
@@ -0,0 +1 @@
+Window
\ No newline at end of file
diff --git a/Testing_Files/3d_line_intersection_test.py b/Testing_Files/3d_line_intersection_test.py
new file mode 100644
index 0000000..25ee45d
--- /dev/null
+++ b/Testing_Files/3d_line_intersection_test.py
@@ -0,0 +1,462 @@
+import numpy as np
+import math
+
+def closestDistanceBetweenLines(a0,a1,b0,b1,clampAll=False,clampA0=False,clampA1=False,clampB0=False,clampB1=False):
+ ''' Given two lines defined by numpy.array pairs (a0,a1,b0,b1)
+ Return distance, the two closest points, and their average
+ '''
+
+ # If clampAll=True, set all clamps to True
+ if clampAll:
+ clampA0=True
+ clampA1=True
+ clampB0=True
+ clampB1=True
+
+ # Calculate denomitator
+ A = a1 - a0
+ B = b1 - b0
+
+ normA = np.linalg.norm(B)
+ if normA == 0:
+ print ("HERE", b0, b1)
+ _A = A / np.linalg.norm(A)
+ _B = B / np.linalg.norm(B)
+ cross = np.cross(_A, _B);
+
+ denom = np.linalg.norm(cross)**2
+
+ # If denominator is 0, lines are parallel: Calculate distance with a projection
+ # and evaluate clamp edge cases
+ if (denom == 0):
+ d0 = np.dot(_A,(b0-a0))
+ d = np.linalg.norm(((d0*_A)+a0)-b0)
+
+ # If clamping: the only time we'll get closest points will be when lines don't overlap at all.
+ # Find if segments overlap using dot products.
+ if clampA0 or clampA1 or clampB0 or clampB1:
+ d1 = np.dot(_A,(b1-a0))
+
+ # Is segment B before A?
+ if d0 <= 0 >= d1:
+ if clampA0 == True and clampB1 == True:
+ if np.absolute(d0) < np.absolute(d1):
+ return b0,a0,np.linalg.norm(b0-a0)
+ return b1,a0,np.linalg.norm(b1-a0)
+
+ # Is segment B after A?
+ elif d0 >= np.linalg.norm(A) <= d1:
+ if clampA1 == True and clampB0 == True:
+ if np.absolute(d0) < np.absolute(d1):
+ return b0,a1,np.linalg.norm(b0-a1)
+ return b1,a1,np.linalg.norm(b1,a1)
+
+ # If clamping is off, or segments overlapped, we have infinite results, just return position.
+ return None,None,d
+
+
+
+ # Lines criss-cross: Calculate the dereminent and return points
+ t = (b0 - a0);
+ det0 = np.linalg.det([t, _B, cross])
+ det1 = np.linalg.det([t, _A, cross])
+
+ t0 = det0/denom;
+ t1 = det1/denom;
+
+ pA = a0 + (_A * t0);
+ pB = b0 + (_B * t1);
+
+ # Clamp results to line segments if needed
+ if clampA0 or clampA1 or clampB0 or clampB1:
+
+ if t0 < 0 and clampA0:
+ pA = a0
+ elif t0 > np.linalg.norm(A) and clampA1:
+ pA = a1
+
+ if t1 < 0 and clampB0:
+ pB = b0
+ elif t1 > np.linalg.norm(B) and clampB1:
+ pB = b1
+
+ d = np.linalg.norm(pA-pB)
+
+ return pA,pB,d
+
+'''
+Calculate the distance of the line segment between the start and end points
+'''
+def distanceBetweenTwoPoints(startPoint, endPoint):
+ x = endPoint[0] - startPoint[0]
+ y = endPoint[1] - startPoint[1]
+ z = endPoint[2] - startPoint[2]
+ return math.sqrt( x*x + y*y + z*z)
+'''
+A function used to reduce a line segment between the startPoint and the endPoint
+by the recduce amount. So if you have a line that is 1 distance long and you want to
+move both of its end points in by .25 this function will return the two new end points
+and the length of the new line will be .5.
+'''
+def reduceLineSegment(startPoint, endPoint, reduceAmount):
+ vector = []
+ '''
+ Parametrize the line segment so that you can subtract the distace. Since the line
+ is parametrized its distance will be from 0 to 1. To reduce it by the desired amount
+ we need to figure out the proportion of the line compared to the parameter and then
+ subtract that amount.
+ '''
+ vector.append(startPoint[0] - endPoint[0]) #Calculate Vector X
+ vector.append(startPoint[1] - endPoint[1]) #Calculate Vector Y
+ vector.append(startPoint[2] - endPoint[2]) #Calculate Vector Z
+ dist = distanceBetweenTwoPoints(startPoint, endPoint)
+ proportion = reduceAmount / dist
+ newX = endPoint[0] + proportion * vector[0]
+ newY = endPoint[1] + proportion * vector[1]
+ newZ = endPoint[2] + proportion * vector[2]
+ newEndPoint = np.array([newX, newY, newZ])
+
+ output = []
+ newX = endPoint[0] + (1 - proportion) * vector[0]
+ newY = endPoint[1] + (1 - proportion) * vector[1]
+ newZ = endPoint[2] + (1 - proportion) * vector[2]
+ newStartPoint = np.array([newX, newY, newZ])
+ output.append(newStartPoint)
+ output.append(newEndPoint)
+ return output
+'''
+Generates the set of second iterated forward difference operator. This comes from Professor Peters' and
+Ji Li's paper. In the paper it is define. It shows up as a triangle with a subscript 2 and and set P next to it
+'''
+def secondIteratedForwardDifferenceOperator(listOfControlPoints, index):
+ deltaSub2Set = []
+ numCtrlPts = len(listOfControlPoints)
+ #Need to wrap around and connect back to the first point
+ for i in range(0, numCtrlPts): #Add -1 to numCtrlPts to make it an open curve again
+ deltaSub2Ofi = listOfControlPoints[(i+2) % numCtrlPts][index] - (2 * listOfControlPoints[(i + 1) % numCtrlPts][index]) + listOfControlPoints[i][index]
+ deltaSub2Set.append(deltaSub2Ofi)
+ return deltaSub2Set
+'''
+This is the l1 - Norm for a set. The l1 norm is just the summation of the absolute value of all the
+elements in the set.
+'''
+def l1Norm(setForNorming):
+ norm = 0
+ i = 0
+ for element in setForNorming:
+ norm += abs(element)
+ i+= 1
+ print ('Terms in L1 Norm: ', i)
+ return norm
+'''
+This generates the Omega one value from Professor Peters' and Ji Li's paper. Omega one is used in combination with the delta from the Denne-Sullivan paper to figure out m1
+'''
+def omegaOne(listOfControlPoints):
+ deltaSetX = secondIteratedForwardDifferenceOperator(listOfControlPoints, 0)
+ deltaSetY = secondIteratedForwardDifferenceOperator(listOfControlPoints, 1)
+ deltaSetZ = secondIteratedForwardDifferenceOperator(listOfControlPoints, 2)
+
+ l1NormX = l1Norm(deltaSetX)
+ l1NormY = l1Norm(deltaSetY)
+ l1NormZ = l1Norm(deltaSetZ)
+
+ return max(l1NormX, l1NormY, l1NormZ)
+'''
+This function generates the m1 value from Professor Peters' and Ji Li's paper. M1 is compared against
+m2 and m3 to determine the number of iterations, j, that need to be done in order for the stick knot
+to properly associate with its correct bezier curve
+'''
+def generateM1(omegaOne, delta):
+ omegaOneSquared = omegaOne * omegaOne
+ deltaSquared = delta * delta
+ intermediate = ((float(7)/float(16) * omegaOneSquared * (1 / deltaSquared)) - (float(1)/ float(7)) )
+ logResult = math.log(intermediate, 2)
+ return math.ceil(logResult)
+
+'''
+Generates the hodograph delta sub 2 set from Professor Peters' and Ji Li's paper. It is defined in the paper
+'''
+def generateHodographDeltaTwoSet(listOfControlPoints, index):
+ hodographDelta2Set = []
+ numControlPoints = len(listOfControlPoints)
+ #Need to wrap around and connect back to the first point
+ for i in range(1, numControlPoints): #Add -1 to numControlPoints to make it an open curve again
+ hodographDeltaSub2Ofi = numControlPoints * (listOfControlPoints[(i + 2) % numControlPoints][index] - (3 * listOfControlPoints[(i+1)% numControlPoints][index]) + (3 * listOfControlPoints[i][index]) - listOfControlPoints[i - 1][index])
+ hodographDelta2Set.append(hodographDeltaSub2Ofi)
+ return hodographDelta2Set
+
+'''
+Found the same way as Omega One but uses the hodograph sets instead
+'''
+def omegaTwo(listOfControlPoints):
+ hodographX = generateHodographDeltaTwoSet(listOfControlPoints, 0)
+ hodographY = generateHodographDeltaTwoSet(listOfControlPoints, 1)
+ hodographZ = generateHodographDeltaTwoSet(listOfControlPoints, 2)
+
+ l1NormX = l1Norm(hodographX)
+ l1NormY = l1Norm(hodographY)
+ l1NormZ = l1Norm(hodographZ)
+
+ return max(l1NormX, l1NormY, l1NormZ)
+
+'''
+The M2 value from Professor Peters' and Ji Li's paper. It is found using the hodograph set
+'''
+def generateM2(omegaTwo, lambdaVal, numCtrlPts):
+ j = 0
+ #This gets the next int higher to what would be an unreducible logarithm
+ while( (numCtrlPts * math.pow(2, 3 * j) + math.pow(2, 2 * j)) < math.pow( omegaTwo/lambdaVal , 2)):
+ j += 1
+ return j
+
+'''
+The M3 value from Professor Peters' and Ji Li's paper. It is very similar to M2 except for the additional sin(pi/ 8) value
+'''
+def generateM3(omegaTwo, lambdaVal, numCtrlPts):
+ j = 0
+ #This gets the next int higher to what would be an unreducible logarithm
+ while( ( numCtrlPts * math.pow(2, 3 * j) + math.pow(2, 2 * j)) < math.pow( (omegaTwo / (math.sin(math.pi / 8) * lambdaVal)), 2 ) ):
+ j+=1
+ return j
+
+if __name__ == '__main__':
+ [testa, testb, testdist] = closestDistanceBetweenLines(np.array([0.0, 0.0, 0.0]), np.array([5.0, 0.0, 0.0]), np.array([6.0, 0.0, 1.0]), np.array([10.0, 0.0, 1.0]), clampAll=True)
+ print ("Done: ",testdist)
+ '''
+ Add the line segments of the control polygon to a list
+ '''
+
+ segmentArray = []
+ segmentArray.append(np.array([1.3076, -3.3320, -2.5072]))
+ segmentArray.append(np.array([-1.3841, 4.6826, 0.9135]))
+ segmentArray.append(np.array([-3.2983, -4.0567, 2.6862]))
+ segmentArray.append(np.array([-0.1233, 2.7683, -2.4636]))
+ segmentArray.append(np.array([3.9080, -4.5334, 1.2264]))
+ segmentArray.append(np.array([-3.9360, -0.4383, -0.9834]))
+ segmentArray.append(np.array([3.2182, 4.2961, 2.1125]))
+ [a, b, dist] = closestDistanceBetweenLines(segmentArray[0], segmentArray[1], segmentArray[2], segmentArray[3], clampAll = True)
+ print ('TEST: a is: ', a, ' b is: ', b, ' dist is: ', dist)
+ '''
+ segmentArray = []
+ segmentArray.append(np.array([1.28318923, -2.84316645, -2.25593748]))
+ segmentArray.append(np.array([1.139367593750000, -2.831090593750000, -2.293403687500000]))
+ segmentArray.append(np.array([0.971135187500000, -2.330181187500000, -2.079607375000000]))
+ segmentArray.append(np.array([0.802902781250000, -1.829271781250000, -1.865811062500000]))
+ segmentArray.append(np.array([0.634670375000000, -1.328362375000000, -1.652014750000000]))
+ segmentArray.append(np.array([0.466437968750000, -0.827452968750000, -1.438218437500000]))
+ segmentArray.append(np.array([0.298205562500000, -0.326543562500000, -1.224422125000000]))
+ segmentArray.append(np.array([0.129973156250000, 0.174365843750000, -1.010625812500000]))
+ segmentArray.append(np.array([-0.038259250000000, 0.675275250000000, -0.796829500000000]))
+ segmentArray.append(np.array([-0.206491656250000, 1.176184656250000, -0.583033187500000]))
+ segmentArray.append(np.array([-0.374724062500000, 1.677094062500000, -0.369236875000000]))
+ segmentArray.append(np.array([-0.542956468750000, 2.178003468750000, -0.155440562500000]))
+ segmentArray.append(np.array([-0.711188875000000, 2.678912875000000, 0.058355750000000]))
+ segmentArray.append(np.array([-0.879421281250000, 3.179822281250000, 0.272152062500000]))
+ segmentArray.append(np.array([-1.047653687500000, 3.680731687500000, 0.485948375000000]))
+ segmentArray.append(np.array([-1.215886093750000, 4.181641093750000, 0.699744687500000]))
+ segmentArray.append(np.array([-1.384118500000000, 4.682550500000000, 0.913541000000000]))
+ segmentArray.append(np.array([-1.50375, 4.13635, 1.02434]))
+ segmentArray.append(np.array([-1.62339, 3.59015, 1.13513]))
+ segmentArray.append(np.array([-1.74303, 3.04394, 1.24592]))
+ segmentArray.append(np.array([-1.86266, 2.49774, 1.35671]))
+ segmentArray.append(np.array([-1.9823, 1.95154, 1.4675]))
+ segmentArray.append(np.array([-2.10194, 1.40534, 1.57829]))
+ segmentArray.append(np.array([-2.22157, 0.859137, 1.68908]))
+ segmentArray.append(np.array([-2.34121, 0.312934, 1.79987]))
+ segmentArray.append(np.array([-2.46084, -0.233267, 1.91066]))
+ segmentArray.append(np.array([-2.58048, -0.779468, 2.02145]))
+ segmentArray.append(np.array([-2.70012, -1.32567, 2.13224]))
+ segmentArray.append(np.array([-2.81976, -1.87187, 2.24303]))
+ segmentArray.append(np.array([-2.9394, -2.41807, 2.35382]))
+ segmentArray.append(np.array([-3.05903, -2.96428, 2.46461]))
+ segmentArray.append(np.array([-3.17867, -3.51048, 2.5754]))
+ segmentArray.append(np.array([-3.2983075,-4.0566825,2.686189]))
+ segmentArray.append(np.array([-3.09987, -3.63013, 2.36433]))
+ segmentArray.append(np.array([-2.90143, -3.20357, 2.04247]))
+ segmentArray.append(np.array([-2.70299, -2.77701, 1.72061]))
+ segmentArray.append(np.array([-2.50455, -2.35045, 1.39875]))
+ segmentArray.append(np.array([-2.30611, -1.92389, 1.07689]))
+ segmentArray.append(np.array([-2.10767, -1.49733, 0.755026]))
+ segmentArray.append(np.array([-1.90924, -1.07077, 0.433165]))
+ segmentArray.append(np.array([-1.7108, -0.644214, 0.111303]))
+ segmentArray.append(np.array([-1.51236, -0.217655, -0.210558]))
+ segmentArray.append(np.array([-1.31393, 0.208903, -0.532419]))
+ segmentArray.append(np.array([-1.11549, 0.635462, -0.854279]))
+ segmentArray.append(np.array([-0.91705, 1.06202, -1.17614]))
+ segmentArray.append(np.array([-0.718613, 1.48858, -1.498]))
+ segmentArray.append(np.array([-0.520175, 1.91514, -1.81986]))
+ segmentArray.append(np.array([-0.321737, 2.3417, -2.14172]))
+ segmentArray.append(np.array([-0.1232995,2.768254,-2.463584]))
+ segmentArray.append(np.array([0.128657, 2.3119, -2.23296]))
+ segmentArray.append(np.array([0.380613, 1.85555, -2.00234]))
+ segmentArray.append(np.array([0.632569, 1.3992, -1.77172]))
+ segmentArray.append(np.array([0.884525, 0.942852, -1.5411]))
+ segmentArray.append(np.array([1.13648, 0.486501, -1.31047]))
+ segmentArray.append(np.array([1.38844, 0.0301505, -1.07985]))
+ segmentArray.append(np.array([1.64039, -0.4262, -0.849228]))
+ segmentArray.append(np.array([1.89235, -0.882551, -0.618607]))
+ segmentArray.append(np.array([2.14431, -1.3389, -0.387985]))
+ segmentArray.append(np.array([2.39626, -1.79525, -0.157363]))
+ segmentArray.append(np.array([2.64821, -2.2516, 0.0732595]))
+ segmentArray.append(np.array([2.90017, -2.70795, 0.303882]))
+ segmentArray.append(np.array([3.15212, -3.1643, 0.534504]))
+ segmentArray.append(np.array([3.40408, -3.62065, 0.765126]))
+ segmentArray.append(np.array([3.65604, -4.077, 0.995748]))
+ segmentArray.append(np.array([3.9079915,-4.533357,1.2263705]))
+ segmentArray.append(np.array([3.41775, -4.27741, 1.08826]))
+ segmentArray.append(np.array([2.9275, -4.02147, 0.950154]))
+ segmentArray.append(np.array([2.43725, -3.76553, 0.812045]))
+ segmentArray.append(np.array([1.947, -3.50958, 0.673937]))
+ segmentArray.append(np.array([1.45675, -3.25364, 0.535829]))
+ segmentArray.append(np.array([0.966502, -2.9977, 0.39772]))
+ segmentArray.append(np.array([0.476253, -2.74175, 0.259611]))
+ segmentArray.append(np.array([-0.0139957, -2.48581, 0.121503]))
+ segmentArray.append(np.array([-0.504244, -2.22986, -0.0166055]))
+ segmentArray.append(np.array([-0.994493, -1.97392, -0.154714]))
+ segmentArray.append(np.array([-1.48474, -1.71798, -0.292822]))
+ segmentArray.append(np.array([-1.97499, -1.46204, -0.430931]))
+ segmentArray.append(np.array([-2.46524, -1.2061, -0.56904]))
+ segmentArray.append(np.array([-2.95549, -0.950156, -0.707148]))
+ segmentArray.append(np.array([-3.44574, -0.694214, -0.845257]))
+ segmentArray.append(np.array([-3.935983,-0.438272,-0.983365]))
+ segmentArray.append(np.array([-3.48885, -0.142371, -0.789876]))
+ segmentArray.append(np.array([-3.04171, 0.153529, -0.596387]))
+ segmentArray.append(np.array([-2.59457, 0.449429, -0.402898]))
+ segmentArray.append(np.array([-2.14744, 0.745329, -0.209409]))
+ segmentArray.append(np.array([-1.7003, 1.04123, -0.01592]))
+ segmentArray.append(np.array([-1.25317, 1.33713, 0.177569]))
+ segmentArray.append(np.array([-0.806037, 1.63303, 0.371058]))
+ segmentArray.append(np.array([-0.358904, 1.92893, 0.564547]))
+ segmentArray.append(np.array([0.0882295, 2.22483, 0.758035]))
+ segmentArray.append(np.array([0.535363, 2.52073, 0.951523]))
+ segmentArray.append(np.array([0.982496, 2.81663, 1.14501]))
+ segmentArray.append(np.array([1.42963, 3.11253, 1.3385]))
+ segmentArray.append(np.array([1.87677, 3.40843, 1.53199]))
+ segmentArray.append(np.array([2.3239, 3.70433, 1.72548]))
+ segmentArray.append(np.array([2.77104, 4.00023, 1.91897]))
+ segmentArray.append(np.array([3.218174000000000, 4.296123000000000, 2.112459500000000]))
+ segmentArray.append(np.array([3.098763125000000, 3.819365312500000, 1.823730781250000]))
+ segmentArray.append(np.array([2.979352250000000, 3.342607625000000, 1.535002062500000]))
+ segmentArray.append(np.array([2.859941375000000, 2.865849937500000, 1.246273343750000]))
+ segmentArray.append(np.array([2.740530500000000, 2.389092250000000, 0.957544625000000]))
+ segmentArray.append(np.array([2.621119625000000, 1.912334562500000, 0.668815906250000]))
+ segmentArray.append(np.array([2.501708750000000, 1.435576875000000, 0.380087187500000]))
+ segmentArray.append(np.array([2.382297875000000, 0.958819187500000, 0.091358468750000]))
+ segmentArray.append(np.array([2.262887000000000, 0.482061500000000, -0.197370250000000]))
+ segmentArray.append(np.array([2.143476125000000, 0.005303812500000, -0.486098968750000]))
+ segmentArray.append(np.array([2.024065250000000, -0.471453875000000, -0.774827687500000]))
+ segmentArray.append(np.array([1.904654375000000, -0.948211562500000, -1.063556406250000]))
+ segmentArray.append(np.array([1.785243500000000, -1.424969250000000, -1.352285125000000]))
+ segmentArray.append(np.array([1.665832625000000, -1.901726937500000, -1.641013843750000]))
+ segmentArray.append(np.array([1.546421750000000, -2.378484625000000, -1.929742562500000]))
+ segmentArray.append(np.array([1.427010875000000, -2.855242312500000, -2.218471281250000]))
+ '''
+
+ minimums = []
+ '''
+ For each line segment, compare it to all non-incident (non-adjacent) line segments. Add the distances
+ to the minimums list.
+ '''
+ numCtrlPointSegments = len(segmentArray)
+ for i in range(0, numCtrlPointSegments):
+ #print'i is: ' ,i
+ j = i + 2
+ while j < numCtrlPointSegments:
+ if(i == 0 and (j+1)% numCtrlPointSegments == 0):
+ j+=1
+ continue
+ #print 'j is: ', j, ' and j + 1 mod 7 is', (j+1)% numCtrlPointSegments
+ [a, b, dist] = closestDistanceBetweenLines(segmentArray[i],segmentArray[i+1],segmentArray[j],segmentArray[(j+1) % numCtrlPointSegments],clampAll=True)
+ print ('a is: ', a, ' b is: ', b, ' dist is: ', dist)
+ #print(dist)
+ minimums.append(dist)
+ j+= 1
+
+ '''
+ Of all the distances, take the minimum. This is the r1 value from the Denne-Sullivan paper
+ '''
+ r1 = min(minimums)
+ print ('r1 is: ', r1)
+ '''
+ Epsilon is a constant that we have pre selected and can change
+ '''
+ epsilon = 1.0
+ '''
+ r2 is also from the Denne-Sullivan paper
+ '''
+ r2 = min(r1/2, epsilon/2)
+ print ('r2 is: ', r2)
+
+ '''
+ For each line segment of the control polygon, remove r2 from each end.
+ '''
+ adjustedSegments = []
+ for i in range(0, numCtrlPointSegments):
+ #print 'i is: ', i, 'and the Line Segment is ', segmentArray[i], segmentArray[(i+1)%numCtrlPointSegments]
+ [segment1, segment2] = reduceLineSegment(segmentArray[i], segmentArray[(i+1)%numCtrlPointSegments], r2)
+ adjustedSegments.append(segment1)
+ adjustedSegments.append(segment2)
+ #print 'The new segment is: ', segment1, segment2
+ i+=1
+ #At this point the segments have been reduced along the vertex. This means that the number of points is 2 times the number of control points. This is because the segments no longer share any common points between themselves
+
+ numAdjustedPoints = len(adjustedSegments)
+ newMinimums = []
+
+ '''
+ Then, for each reduced segment find the distance from that segment to all other reduced segments
+ '''
+ i = 0
+ while i + 3 < numAdjustedPoints:
+ j = i + 2
+ while j+1 < numAdjustedPoints:
+ #print 'Comparing: ', adjustedSegments[i], adjustedSegments[i+1], 'to: ', adjustedSegments[j],adjustedSegments[j+1]
+ [a, b, dist] = closestDistanceBetweenLines(adjustedSegments[i],adjustedSegments[i+1],adjustedSegments[j],adjustedSegments[j+1],clampAll=True)
+ newMinimums.append(dist)
+ #print dist
+ j+=2
+ i+=2
+
+ '''
+ Take r3 (also from Denne-Sullivan) to be the minimum distance of all distances between line segments
+ '''
+ r3 = min(newMinimums)
+ print ('r3 is: ', r3)
+
+ '''
+ r4 is also from Denne-Sullivan paper
+ '''
+ r4 = r3 / 6
+ print ('r4 is: ', r4)
+
+ '''
+ Finally, calculate delta (from Denne-Sullivan)
+ '''
+ delta = r4 / 3
+ print ('delta is: ', delta)
+
+ omega1 = omegaOne(segmentArray)
+ print ('Omega One is: ', omega1)
+ m1 = generateM1(omega1, delta)
+ print ('M1 is: ', m1)
+
+ segmentLengths = []
+ for i in range(0, numCtrlPointSegments):
+ segmentLengths.append(distanceBetweenTwoPoints(segmentArray[i], segmentArray[(i +1) % numCtrlPointSegments]))
+
+ lambdaVal = min(segmentLengths)
+ print ('Lambda is: ', lambdaVal)
+ omega2 = omegaTwo(segmentArray)
+ print ('Omega Two is: ', omega2)
+ m2 = generateM2(omega2, lambdaVal, numCtrlPointSegments)
+ print ('M2 is: ', m2)
+
+ m3 = generateM3(omega2, lambdaVal, numCtrlPointSegments)
+ print ('M3 is: ', m3)
+
+
+
+
+
+
+
diff --git a/Testing_Files/TJP-4_1_stick-unknot-halved-1.curve b/Testing_Files/TJP-4_1_stick-unknot-halved-1.curve
new file mode 100644
index 0000000..16f6d44
--- /dev/null
+++ b/Testing_Files/TJP-4_1_stick-unknot-halved-1.curve
@@ -0,0 +1,33 @@
+ 1.307600000000000, -3.332000000000000, -2.507200000000000
+
+-0.0765185, 0.67527525, -0.7968295
+
+ -1.384118500000000, 4.682550500000000, 0.913541000000000
+-1.44393425, 4.40945025, 0.9689405
+
+
+-1.50375, 4.13635, 1.02434
+-2.40102875, 0.03983375, 1.8552645
+
+
+-3.2983075,-4.0566825,2.686189
+-1.7108035, -0.64421425, 0.1113025
+
+
+-0.1232995,2.768254,-2.463584
+1.892346, -0.8825515, -0.61860675
+
+
+3.9079915,-4.533357,1.2263705
+-0.01399575, -2.4858145, 0.12150275
+
+
+-3.935983,-0.438272,-0.983365
+-0.3589045, 1.9289255, 0.56454725
+
+
+3.218174000000000, 4.296123000000000, 2.112459500000000
+2.262887, 0.4820615, -0.19737025
+
+
+1.307600000000000, -3.332000000000000, -2.507200000000000
diff --git a/Testing_Files/TJP-4_1_stick-unknot-halved-2.curve b/Testing_Files/TJP-4_1_stick-unknot-halved-2.curve
new file mode 100644
index 0000000..083c2bd
--- /dev/null
+++ b/Testing_Files/TJP-4_1_stick-unknot-halved-2.curve
@@ -0,0 +1,65 @@
+ 1.307600000000000, -3.332000000000000, -2.507200000000000
+
+0.634670375,-1.328362375,-1.65201475
+
+-0.03825925,0.67527525,-0.7968295
+-0.711188875,2.678912875,0.05835575
+
+
+ -1.384118500000000, 4.682550500000000, 0.913541000000000
+-1.414026375,4.546000375,0.94124075
+
+
+-1.44393425,4.40945025,0.9689405
+-1.473842125,4.272900125,0.99664025
+
+
+-1.50375,4.13635,1.02434
+-1.952389375,2.088091875,1.43980225
+
+
+-2.40102875,0.03983375,1.8552645
+-2.849668125,-2.008424375,2.27072675
+
+
+-3.2983075,-4.0566825,2.686189
+-2.5045555,-2.350448375,1.39874575
+
+
+-1.7108035,-0.64421425,0.1113025
+-0.9170515,1.062019875,-1.17614075
+
+
+-0.1232995,2.768254,-2.463584
+0.88452325,0.94285125,-1.541095375
+
+
+1.892346,-0.8825515,-0.61860675
+2.90016875,-2.70795425,0.303881875
+
+
+3.9079915,-4.533357,1.2263705
+1.946997875,-3.50958575,0.673936625
+
+
+-0.01399575,-2.4858145,0.12150275
+-1.974989375,-1.46204325,-0.430931125
+
+
+-3.935983,-0.438272,-0.983365
+-2.14744375,0.74532675,-0.209408875
+
+
+-0.3589045,1.9289255,0.56454725
+1.42963475,3.11252425,1.338503375
+
+
+3.218174000000000, 4.296123000000000, 2.112459500000000
+2.7405305,2.38909225,0.957544625
+
+
+2.262887,0.4820615,-0.19737025
+1.7852435,-1.42496925,-1.352285125
+
+
+1.307600000000000, -3.332000000000000, -2.507200000000000
diff --git a/Testing_Files/TJP-4_1_stick-unknot-halved-3.curve b/Testing_Files/TJP-4_1_stick-unknot-halved-3.curve
new file mode 100644
index 0000000..92bb3e9
--- /dev/null
+++ b/Testing_Files/TJP-4_1_stick-unknot-halved-3.curve
@@ -0,0 +1,130 @@
+ 1.307600000000000, -3.332000000000000, -2.507200000000000
+
+0.9711351875,-2.3301811875,-2.079607375
+
+0.634670375,-1.328362375,-1.65201475
+0.2982055625,-0.3265435625,-1.224422125
+
+
+-0.03825925,0.67527525,-0.7968295
+-0.3747240625,1.6770940625,-0.369236875
+
+
+-0.711188875,2.678912875,0.05835575
+-1.0476536875,3.6807316875,0.485948375
+
+
+ -1.384118500000000, 4.682550500000000, 0.913541000000000
+-1.3990724375,4.6142754375,0.927390875
+
+
+-1.414026375,4.546000375,0.94124075
+-1.4289803125,4.4777253125,0.955090625
+
+
+-1.44393425,4.40945025,0.9689405
+-1.4588881875,4.3411751875,0.982790375
+
+
+-1.473842125,4.272900125,0.99664025
+-1.4887960625,4.2046250625,1.010490125
+
+
+-1.50375,4.13635,1.02434
+-1.7280696875,3.1122209375,1.232071125
+
+
+-1.952389375,2.088091875,1.43980225
+-2.1767090625,1.0639628125,1.647533375
+
+
+-2.40102875,0.03983375,1.8552645
+-2.6253484375,-0.9842953125,2.062995625
+
+
+-2.849668125,-2.008424375,2.27072675
+-3.0739878125,-3.0325534375,2.478457875
+
+
+-3.2983075,-4.0566825,2.686189
+-2.9014315,-3.2035654375,2.042467375
+
+
+-2.5045555,-2.350448375,1.39874575
+-2.1076795,-1.4973313125,0.755024125
+
+
+-1.7108035,-0.64421425,0.1113025
+-1.3139275,0.2089028125,-0.532419125
+
+
+-0.9170515,1.062019875,-1.17614075
+-0.5201755,1.9151369375,-1.819862375
+
+
+-0.1232995,2.768254,-2.463584
+0.380611875,1.855552625,-2.0023396875
+
+
+0.88452325,0.94285125,-1.541095375
+1.388434625,0.030149875,-1.0798510625
+
+
+1.892346,-0.8825515,-0.61860675
+2.396257375,-1.795252875,-0.1573624375
+
+
+2.90016875,-2.70795425,0.303881875
+3.404080125,-3.620655625,0.7651261875
+
+
+3.9079915,-4.533357,1.2263705
+2.9274946875,-4.021471375,0.9501535625
+
+
+1.946997875,-3.50958575,0.673936625
+0.9665010625,-2.997700125,0.3977196875
+
+
+-0.01399575,-2.4858145,0.12150275
+-0.9944925625,-1.973928875,-0.1547141875
+
+
+-1.974989375,-1.46204325,-0.430931125
+-2.9554861875,-0.950157625,-0.7071480625
+
+
+-3.935983,-0.438272,-0.983365
+-3.041713375,0.153527375,-0.5963869375
+
+
+-2.14744375,0.74532675,-0.209408875
+-1.253174125,1.337126125,0.1775691875
+
+
+-0.3589045,1.9289255,0.56454725
+0.535365125,2.520724875,0.9515253125
+
+
+1.42963475,3.11252425,1.338503375
+2.323904375,3.704323625,1.7254814375
+
+
+3.218174000000000, 4.296123000000000, 2.112459500000000
+2.97935225,3.342607625,1.5350020625
+
+
+2.7405305,2.38909225,0.957544625
+2.50170875,1.435576875,0.3800871875
+
+
+2.262887,0.4820615,-0.19737025
+2.02406525,-0.471453875,-0.7748276875
+
+
+1.7852435,-1.42496925,-1.352285125
+1.54642175,-2.378484625,-1.9297425625
+
+
+1.307600000000000, -3.332000000000000, -2.507200000000000
+
diff --git a/Testing_Files/TJP-4_1_stick-unknot.curve b/Testing_Files/TJP-4_1_stick-unknot.curve
new file mode 100644
index 0000000..e1d9f92
--- /dev/null
+++ b/Testing_Files/TJP-4_1_stick-unknot.curve
@@ -0,0 +1,16 @@
+ %ignore me
+ 1.307600000000000, -3.332000000000000, -2.507200000000000
+ -1.384118500000000, 4.682550500000000, 0.913541000000000
+
+
+
+-3.2983075,-4.0566825,2.686189
+
+-0.1232995,2.768254,-2.463584
+
+3.9079915,-4.533357,1.2263705
+
+-3.935983,-0.438272,-0.983365
+
+3.218174000000000, 4.296123000000000, 2.112459500000000
+
diff --git a/Testing_Files/TJP-4_1_stick-unknot_TEST.curve b/Testing_Files/TJP-4_1_stick-unknot_TEST.curve
new file mode 100644
index 0000000..c7f3fb2
--- /dev/null
+++ b/Testing_Files/TJP-4_1_stick-unknot_TEST.curve
@@ -0,0 +1,28 @@
+1.3076, -3.332, -2.5072
+
+-0.038259268, 0.6752752, -0.79682946
+
+-1.3841186, 4.6825504, 0.913541
+
+-2.341213, 0.31293392, 1.799865
+
+-3.2983074, -4.0566826, 2.686189
+
+-1.7108035, -0.6442143, 0.111302495
+
+-0.1232995, 2.768254, -2.463584
+
+1.8923459, -0.88255155, -0.61860675
+
+3.9079914, -4.533357, 1.2263705
+
+-0.013995767, -2.4858146, 0.12150273
+
+-3.935983, -0.438272, -0.983365
+
+-0.35890448, 1.9289255, 0.5645472
+
+3.218174, 4.296123, 2.1124594
+
+2.262887, 0.4820615, -0.19737029
+
diff --git a/Testing_Files/TJP-4th-C1-high-precision.curve b/Testing_Files/TJP-4th-C1-high-precision.curve
new file mode 100644
index 0000000..1ac6bbb
--- /dev/null
+++ b/Testing_Files/TJP-4th-C1-high-precision.curve
@@ -0,0 +1,194 @@
+ 1.28318923, -2.84316645, -2.25593748
+ 1.139367593750000, -2.831090593750000, -2.293403687500000
+ 0.971135187500000, -2.330181187500000, -2.079607375000000
+ 0.802902781250000, -1.829271781250000, -1.865811062500000
+ 0.634670375000000, -1.328362375000000, -1.652014750000000
+ 0.466437968750000, -0.827452968750000, -1.438218437500000
+ 0.298205562500000, -0.326543562500000, -1.224422125000000
+ 0.129973156250000, 0.174365843750000, -1.010625812500000
+ -0.038259250000000, 0.675275250000000, -0.796829500000000
+ -0.206491656250000, 1.176184656250000, -0.583033187500000
+ -0.374724062500000, 1.677094062500000, -0.369236875000000
+ -0.542956468750000, 2.178003468750000, -0.155440562500000
+ -0.711188875000000, 2.678912875000000, 0.058355750000000
+ -0.879421281250000, 3.179822281250000, 0.272152062500000
+ -1.047653687500000, 3.680731687500000, 0.485948375000000
+ -1.215886093750000, 4.181641093750000, 0.699744687500000
+ -1.384118500000000, 4.682550500000000, 0.913541000000000
+
+-1.50375, 4.13635, 1.02434
+
+-1.62339, 3.59015, 1.13513
+
+-1.74303, 3.04394, 1.24592
+
+-1.86266, 2.49774, 1.35671
+
+-1.9823, 1.95154, 1.4675
+
+-2.10194, 1.40534, 1.57829
+
+-2.22157, 0.859137, 1.68908
+
+-2.34121, 0.312934, 1.79987
+
+-2.46084, -0.233267, 1.91066
+
+-2.58048, -0.779468, 2.02145
+
+-2.70012, -1.32567, 2.13224
+
+-2.81976, -1.87187, 2.24303
+
+-2.9394, -2.41807, 2.35382
+
+-3.05903, -2.96428, 2.46461
+
+-3.17867, -3.51048, 2.5754
+
+-3.2983075,-4.0566825,2.686189
+
+-3.09987, -3.63013, 2.36433
+
+-2.90143, -3.20357, 2.04247
+
+-2.70299, -2.77701, 1.72061
+
+-2.50455, -2.35045, 1.39875
+
+-2.30611, -1.92389, 1.07689
+
+-2.10767, -1.49733, 0.755026
+
+-1.90924, -1.07077, 0.433165
+
+-1.7108, -0.644214, 0.111303
+
+-1.51236, -0.217655, -0.210558
+
+-1.31393, 0.208903, -0.532419
+
+-1.11549, 0.635462, -0.854279
+
+-0.91705, 1.06202, -1.17614
+
+-0.718613, 1.48858, -1.498
+
+-0.520175, 1.91514, -1.81986
+
+-0.321737, 2.3417, -2.14172
+
+-0.1232995,2.768254,-2.463584
+
+0.128657, 2.3119, -2.23296
+
+0.380613, 1.85555, -2.00234
+
+0.632569, 1.3992, -1.77172
+
+0.884525, 0.942852, -1.5411
+
+1.13648, 0.486501, -1.31047
+
+1.38844, 0.0301505, -1.07985
+
+1.64039, -0.4262, -0.849228
+
+1.89235, -0.882551, -0.618607
+
+2.14431, -1.3389, -0.387985
+
+2.39626, -1.79525, -0.157363
+
+2.64821, -2.2516, 0.0732595
+
+2.90017, -2.70795, 0.303882
+
+3.15212, -3.1643, 0.534504
+
+3.40408, -3.62065, 0.765126
+
+3.65604, -4.077, 0.995748
+
+3.9079915,-4.533357,1.2263705
+
+3.41775, -4.27741, 1.08826
+
+2.9275, -4.02147, 0.950154
+
+2.43725, -3.76553, 0.812045
+
+1.947, -3.50958, 0.673937
+
+1.45675, -3.25364, 0.535829
+
+0.966502, -2.9977, 0.39772
+
+0.476253, -2.74175, 0.259611
+
+-0.0139957, -2.48581, 0.121503
+
+-0.504244, -2.22986, -0.0166055
+
+-0.994493, -1.97392, -0.154714
+
+-1.48474, -1.71798, -0.292822
+
+-1.97499, -1.46204, -0.430931
+
+-2.46524, -1.2061, -0.56904
+
+-2.95549, -0.950156, -0.707148
+
+-3.44574, -0.694214, -0.845257
+
+-3.935983,-0.438272,-0.983365
+
+-3.48885, -0.142371, -0.789876
+
+-3.04171, 0.153529, -0.596387
+
+-2.59457, 0.449429, -0.402898
+
+-2.14744, 0.745329, -0.209409
+
+-1.7003, 1.04123, -0.01592
+
+-1.25317, 1.33713, 0.177569
+
+-0.806037, 1.63303, 0.371058
+
+-0.358904, 1.92893, 0.564547
+
+0.0882295, 2.22483, 0.758035
+
+0.535363, 2.52073, 0.951523
+
+0.982496, 2.81663, 1.14501
+
+1.42963, 3.11253, 1.3385
+
+1.87677, 3.40843, 1.53199
+
+2.3239, 3.70433, 1.72548
+
+2.77104, 4.00023, 1.91897
+
+
+ 3.218174000000000, 4.296123000000000, 2.112459500000000
+ 3.098763125000000, 3.819365312500000, 1.823730781250000
+ 2.979352250000000, 3.342607625000000, 1.535002062500000
+ 2.859941375000000, 2.865849937500000, 1.246273343750000
+ 2.740530500000000, 2.389092250000000, 0.957544625000000
+ 2.621119625000000, 1.912334562500000, 0.668815906250000
+ 2.501708750000000, 1.435576875000000, 0.380087187500000
+ 2.382297875000000, 0.958819187500000, 0.091358468750000
+ 2.262887000000000, 0.482061500000000, -0.197370250000000
+ 2.143476125000000, 0.005303812500000, -0.486098968750000
+ 2.024065250000000, -0.471453875000000, -0.774827687500000
+ 1.904654375000000, -0.948211562500000, -1.063556406250000
+ 1.785243500000000, -1.424969250000000, -1.352285125000000
+ 1.665832625000000, -1.901726937500000, -1.641013843750000
+ 1.546421750000000, -2.378484625000000, -1.929742562500000
+ 1.427010875000000, -2.855242312500000, -2.218471281250000
+ 1.28318923, -2.84316645, -2.25593748
diff --git a/Testing_Files/test.curve b/Testing_Files/test.curve
new file mode 100644
index 0000000..27f824e
--- /dev/null
+++ b/Testing_Files/test.curve
@@ -0,0 +1,17 @@
+ %ignore me
+ 1.307600000000000, -3.332000000000000, -2.507200000000000
+ -1.384118500000000, 4.682550500000000, 0.913541000000000
+
+
+
+-3.2983075,-4.0566825,2.686189
+
+-0.1232995,2.768254,-2.463584
+
+3.9079915,-4.533357,1.2263705
+
+-3.935983,-0.438272,-0.983365
+
+3.218174000000000, 4.296123000000000, 2.112459500000000
+ 1.307600000000000, -3.332000000000000, -2.507200000000000
+
diff --git a/WebContent/META-INF/MANIFEST.MF b/WebContent/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..254272e
--- /dev/null
+++ b/WebContent/META-INF/MANIFEST.MF
@@ -0,0 +1,3 @@
+Manifest-Version: 1.0
+Class-Path:
+
diff --git a/junit_tests/com/knotrenderer/modeltest/BezierCurveTest.java b/junit_tests/com/knotrenderer/modeltest/BezierCurveTest.java
new file mode 100644
index 0000000..5b0cb00
--- /dev/null
+++ b/junit_tests/com/knotrenderer/modeltest/BezierCurveTest.java
@@ -0,0 +1,80 @@
+package com.knotrenderer.modeltest;
+
+import static org.junit.Assert.*;
+
+import java.util.ArrayList;
+
+import org.junit.Test;
+
+import com.knotrenderer.model.BezierCurve;
+import com.knotrenderer.model.Curve;
+import com.knotrenderer.util.CurveLoader;
+import com.knotrenderer.utiltest.TestUtils;
+
+import processing.core.PVector;
+
+public class BezierCurveTest
+{
+// private static final String CURVE_PATH = "/Users/peterzaffetti/UC/Knot-Renderer-Processing/Testing_Files/test.curve";
+// private static final String CURVE_PATH = "D:/Peter/College_Grad_School/Independent_Study_Spring_2016_Cont/Knot-Renderer-Processing/Testing_Files/test.curve";
+ private static final String CURVE_PATH = "D:/Peter/College_Grad_School/Independent_Study_Spring_2016_Cont/Knot-Renderer-Processing/Testing_Files/TJP-4th-C1-high-precision.curve";
+
+ private static final Curve CURVE = CurveLoader.loadCurveFromFile(CURVE_PATH);
+ private static final BezierCurve BEZIER_CURVE = new BezierCurve(CURVE.getKnotPoints());
+
+ private static final PVector CONTROL_POINT_0 = CURVE.getKnotPoints().get(0);
+ private static final PVector CONTROL_POINT_1 = CURVE.getKnotPoints().get(1);
+ private static final PVector CONTROL_POINT_2 = CURVE.getKnotPoints().get(2);
+ private static final PVector CONTROL_POINT_3 = CURVE.getKnotPoints().get(3);
+ private static final PVector CONTROL_POINT_4 = CURVE.getKnotPoints().get(4);
+ private static final PVector CONTROL_POINT_5 = CURVE.getKnotPoints().get(5);
+ private static final PVector CONTROL_POINT_6 = CURVE.getKnotPoints().get(6);
+ private static final PVector CONTROL_POINT_7 = CURVE.getKnotPoints().get(7);
+
+
+ @Test
+ public void calculateBezierCurveValueTestRecursive_StartAndEndEqual()
+ {
+ final PVector startPoint = BEZIER_CURVE.getCurveValueRecursively(0);
+ final PVector endPoint = BEZIER_CURVE.getCurveValueRecursively(1);
+ assertTrue("Start and End point were not the same. Start point was " + startPoint + " and End point was: " + endPoint,
+ startPoint.equals(endPoint));
+ }
+
+ @Test
+ public void calculateBezierCurveValueTestSummation_StartAndEndEqual()
+ {
+ final PVector startPoint = BEZIER_CURVE.getCurveValueFromSummationExpansion(0);
+ final PVector endPoint = BEZIER_CURVE.getCurveValueFromSummationExpansion(1);
+ assertTrue("Start and End point were not the same. Start point was " + startPoint + " and End point was: " + endPoint,
+ startPoint.equals(endPoint));
+ }
+
+ /**
+ * A test to make sure that both recursive generation and summation generation of the values is the same.
+ */
+ @Test
+ public void calculateBezierCurveValue_EqualityOfBothMethods()
+ {
+ final double epsilon = 0.1;
+ final ArrayList recursivePoints = new ArrayList();
+ final ArrayList summationPoints = new ArrayList();
+
+ for(double i = 0; i <= 1; i+=0.001)
+ {
+ recursivePoints.add(BEZIER_CURVE.getCurveValueRecursively(i));
+ summationPoints.add(BEZIER_CURVE.getCurveValueFromSummationExpansion(i));
+ }
+
+ System.out.println(recursivePoints.size());
+ for(int i = 0; i < recursivePoints.size(); i++)
+ {
+ PVector recursivePoint = recursivePoints.get(i);
+ PVector summationPoint = summationPoints.get(i);
+ System.out.println("i: " + i + " Recursive: " + recursivePoint + " Summation: " + summationPoint);
+ assertTrue("Points were not equal. Recursive Point was: " + recursivePoint + " and Summation Point was: " + summationPoint,
+ TestUtils.areVectorsEqualWithEpsilon(recursivePoint, summationPoint, epsilon));
+ }
+ }
+
+}
diff --git a/junit_tests/com/knotrenderer/modeltest/LineSegmentTest.java b/junit_tests/com/knotrenderer/modeltest/LineSegmentTest.java
new file mode 100644
index 0000000..1036813
--- /dev/null
+++ b/junit_tests/com/knotrenderer/modeltest/LineSegmentTest.java
@@ -0,0 +1,117 @@
+package com.knotrenderer.modeltest;
+
+import static org.junit.Assert.*;
+
+import org.junit.Test;
+
+import com.knotrenderer.model.LineSegment;
+
+import processing.core.PVector;
+
+
+public class LineSegmentTest {
+ private final double epsilon = 0.0001;
+
+ /**
+ * Begin:
+ * LineSegment Closest distance tests- these test will determine the closest distance (line segment) given two line segments.
+ * It currently only tests with clamping turned on. Clamping is where you only want a value on either line
+ * segment (you resulting line segment must have its end points on either line segment).
+ *
+ */
+ @Test
+ public void closestDistanceTest() {
+ final double expectedLength = 0.924675529894;
+
+ PVector vector1 = new PVector(1.3076f, -3.3320f, -2.5072f);
+ PVector vector2 = new PVector(-1.3842f, 4.6826f, 0.9135f);
+ PVector vector3 = new PVector(-3.2982f,-4.0567f, 2.6862f);
+ PVector vector4 = new PVector(-0.1233f, 2.7683f, -2.4636f);
+
+ LineSegment testSegment1 = new LineSegment(vector1, vector2);
+ LineSegment testSegment2 = new LineSegment(vector3, vector4);
+ LineSegment resultSegment = testSegment1.closestDistanceToLineSegment(testSegment2);
+ assertTrue(resultSegment.getLength() - epsilon < expectedLength && resultSegment.getLength() + epsilon > expectedLength);
+ }
+
+ @Test
+ public void closestDistanceTest_Parallel()
+ {
+ final double expectedLength = 2.0;
+
+ PVector vector1 = new PVector(0.f, 0.f, 0.f);
+ PVector vector2 = new PVector(1.f, 0.f, 0.f);
+ PVector vector3 = new PVector(0.f, 2.f, 0.f);
+ PVector vector4 = new PVector(1.f, 2.f, 0.f);
+
+ LineSegment testSegment1 = new LineSegment(vector1, vector2);
+ LineSegment testSegment2 = new LineSegment(vector3, vector4);
+ LineSegment resultSegment = testSegment1.closestDistanceToLineSegment(testSegment2);
+ assertTrue(resultSegment.getLength() - epsilon < expectedLength && resultSegment.getLength() + epsilon > expectedLength);
+ }
+
+ @Test
+ public void closestDistanceTest_SharedEndPoint()
+ {
+ final double expectedLength = 0.0;
+
+ PVector vector1 = new PVector(0.f, 0.f, 0.f);
+ PVector vector2 = new PVector(1.f, 0.f, 0.f);
+ PVector vector3 = new PVector(0.f, 0.f, 0.f);
+ PVector vector4 = new PVector(0.f, 2.f, 0.f);
+
+ LineSegment testSegment1 = new LineSegment(vector1, vector2);
+ LineSegment testSegment2 = new LineSegment(vector3, vector4);
+ LineSegment resultSegment = testSegment1.closestDistanceToLineSegment(testSegment2);
+ assertTrue(resultSegment.getLength() - epsilon < expectedLength && resultSegment.getLength() + epsilon > expectedLength);
+ }
+
+ @Test
+ public void closestDistanceTest_OrthogonalStraightZ()
+ {
+ final double expectedLength = 2.0;
+
+ PVector vector1 = new PVector(0.f, 0.f, 0.f);
+ PVector vector2 = new PVector(1.f, 0.f, 0.f);
+ PVector vector3 = new PVector(0.5f, 2.f, -2.f);
+ PVector vector4 = new PVector(0.5f, 2.f, 2.f);
+
+ LineSegment testSegment1 = new LineSegment(vector1, vector2);
+ LineSegment testSegment2 = new LineSegment(vector3, vector4);
+ LineSegment resultSegment = testSegment1.closestDistanceToLineSegment(testSegment2);
+ assertTrue(resultSegment.getLength() - epsilon < expectedLength && resultSegment.getLength() + epsilon > expectedLength);
+ }
+
+
+ @Test
+ public void closestDistanceTest_OrthogonalSlantingZ()
+ {
+ final double expectedLength = 5.0990195135;
+
+ PVector vector1 = new PVector(0.f, 0.f, 0.f);
+ PVector vector2 = new PVector(1.f, 0.f, 0.f);
+ PVector vector3 = new PVector(2.f, 5.f, -2.f);
+ PVector vector4 = new PVector(2.f, 5.f, 2.f);
+
+ LineSegment testSegment1 = new LineSegment(vector1, vector2);
+ LineSegment testSegment2 = new LineSegment(vector3, vector4);
+ LineSegment resultSegment = testSegment1.closestDistanceToLineSegment(testSegment2);
+ assertTrue(resultSegment.getLength() - epsilon < expectedLength && resultSegment.getLength() + epsilon > expectedLength);
+ }
+
+ @Test
+ public void closestDistanceTest_SameLine()
+ {
+ final double expectedLength = 0;
+
+ PVector vector1 = new PVector(0.f, 0.f, 0.f);
+ PVector vector2 = new PVector(1.f, 0.f, 0.f);
+
+ LineSegment testSegment1 = new LineSegment(vector1, vector2);
+ LineSegment testSegment2 = new LineSegment(vector1, vector2);
+ LineSegment resultSegment = testSegment1.closestDistanceToLineSegment(testSegment2);
+ assertTrue(resultSegment.getLength() - epsilon < expectedLength && resultSegment.getLength() + epsilon > expectedLength);
+ }
+
+
+}
diff --git a/junit_tests/com/knotrenderer/utiltest/CurveLoaderTest.java b/junit_tests/com/knotrenderer/utiltest/CurveLoaderTest.java
new file mode 100644
index 0000000..a5d7641
--- /dev/null
+++ b/junit_tests/com/knotrenderer/utiltest/CurveLoaderTest.java
@@ -0,0 +1,16 @@
+package com.knotrenderer.utiltest;
+
+import static org.junit.Assert.*;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+public class CurveLoaderTest {
+
+ @Test
+ public void test() {
+// fail("Not yet implemented");
+ Assert.assertTrue(true);
+ }
+
+}
diff --git a/junit_tests/com/knotrenderer/utiltest/MathUtilTest.java b/junit_tests/com/knotrenderer/utiltest/MathUtilTest.java
new file mode 100644
index 0000000..45126c2
--- /dev/null
+++ b/junit_tests/com/knotrenderer/utiltest/MathUtilTest.java
@@ -0,0 +1,81 @@
+package com.knotrenderer.utiltest;
+
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import com.knotrenderer.model.BezierCurve;
+import com.knotrenderer.model.Curve;
+import com.knotrenderer.util.CurveLoader;
+import com.knotrenderer.util.MathUtil;
+
+import processing.core.PMatrix3D;
+import processing.core.PVector;
+
+public class MathUtilTest
+{
+ private static final String CURVE_PATH = "/Users/peterzaffetti/UC/Knot-Renderer-Processing/Testing_Files/test.curve";
+// private static final String CURVE_PATH = "D:/Peter/College_Grad_School/Independent_Study_Spring_2016_Cont/Knot-Renderer-Processing/Testing_Files/test.curve";
+ private static final Curve CURVE = CurveLoader.loadCurveFromFile(CURVE_PATH);
+ private static final BezierCurve BEZIER_CURVE = new BezierCurve(CURVE.getKnotPoints());
+
+ private static final PVector CONTROL_POINT_0 = CURVE.getKnotPoints().get(0);
+ private static final PVector CONTROL_POINT_1 = CURVE.getKnotPoints().get(1);
+ private static final PVector CONTROL_POINT_2 = CURVE.getKnotPoints().get(2);
+ private static final PVector CONTROL_POINT_3 = CURVE.getKnotPoints().get(3);
+ private static final PVector CONTROL_POINT_4 = CURVE.getKnotPoints().get(4);
+ private static final PVector CONTROL_POINT_5 = CURVE.getKnotPoints().get(5);
+ private static final PVector CONTROL_POINT_6 = CURVE.getKnotPoints().get(6);
+ private static final PVector CONTROL_POINT_7 = CURVE.getKnotPoints().get(7);
+
+ private static final double epsilon = 1.0;
+
+ @Test
+ public void l1NormTest()
+ {
+ double expectedNorm = 452.3;
+ double[] values = {100.0, -50.0, 15.4, 17.3, -257.3, 12.4};
+ double norm = MathUtil.calculateL1Norm(values);
+ assertTrue(TestUtils.isEqualWithEpsilon(norm, expectedNorm, epsilon));
+ }
+
+ @Test
+ public void l2NormTest()
+ {
+ double expectedNorm = 264.57513110645;
+ double[] values = {100.0, 100.0, 100.0, 100.0, 100.0, 100.d /*pretty cool - you can specify double by putting the d*/, 100.0};
+ double norm = MathUtil.calculateL2Norm(values);
+ assertTrue(TestUtils.isEqualWithEpsilon(norm, expectedNorm, epsilon));
+ }
+
+ @Test
+ public void determinant3x3Test()
+ {
+ PMatrix3D testMatrix = new PMatrix3D(6, 1, 1, 0, 4, -2, 5, 0, 2, 8, 7, 0, 0, 0, 0, 0);
+ double determinant = MathUtil.calculate3x3Determinant(testMatrix);
+ assertTrue(determinant == -306);
+ }
+
+ @Test
+ public void binomialCoefficientAsDoubleTest()
+ {
+ double expectedResult = 535983370403809682970d;
+ double actualResult = MathUtil.binomialCoefficientAsDouble(100, 20);
+ assertTrue(TestUtils.isEqualWithEpsilon(expectedResult, actualResult, 100000)); //PDZ- this has to be 100000 since it loses precision in scientific notation for some reason
+
+ expectedResult = 1;
+ actualResult = MathUtil.binomialCoefficientAsDouble(100, 0);
+ assertTrue(actualResult == expectedResult);
+
+ actualResult = MathUtil.binomialCoefficientAsDouble(100, 100);
+ assertTrue(actualResult == expectedResult);
+ }
+
+ @Test
+ public void binomialCoefficientAsDoubleTest_ControlPoints()
+ {
+
+ }
+
+
+}
diff --git a/junit_tests/com/knotrenderer/utiltest/OptimumIterationGeneratorTest.java b/junit_tests/com/knotrenderer/utiltest/OptimumIterationGeneratorTest.java
new file mode 100644
index 0000000..d315a7d
--- /dev/null
+++ b/junit_tests/com/knotrenderer/utiltest/OptimumIterationGeneratorTest.java
@@ -0,0 +1,258 @@
+package com.knotrenderer.utiltest;
+
+import java.util.ArrayList;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import com.knotrenderer.model.LineSegment;
+import com.knotrenderer.util.OptimumIterationGenerator;
+
+import processing.core.PVector;
+
+public class OptimumIterationGeneratorTest
+{
+ private static final double epsilon = 0.001;
+ private static final double userEpsilon = 1.00;
+ private static PVector vector1 = new PVector(1.307600000000000f, -3.332000000000000f, -2.507200000000000f);
+ private static PVector vector2 = new PVector(-1.384118500000000f, 4.682550500000000f, 0.913541000000000f);
+ private static PVector vector3 = new PVector(-3.2983075f, -4.0566825f, 2.686189f);
+ private static PVector vector4 = new PVector(-0.1232995f, 2.768254f, -2.463584f);
+ private static PVector vector5 = new PVector(3.9079915f, -4.533357f, 1.2263705f);
+ private static PVector vector6 = new PVector(-3.935983f, -0.438272f, -0.983365f);
+ private static PVector vector7 = new PVector(3.218174000000000f, 4.296123000000000f, 2.112459500000000f);
+ private static PVector vector8 = new PVector(1.307600000000000f, -3.332000000000000f, -2.507200000000000f);
+
+
+ private static ArrayList knotPoints = new ArrayList();
+ static{
+ knotPoints.add(vector1);
+ knotPoints.add(vector2);
+ knotPoints.add(vector3);
+ knotPoints.add(vector4);
+ knotPoints.add(vector5);
+ knotPoints.add(vector6);
+ knotPoints.add(vector7);
+ knotPoints.add(vector8);
+ }
+
+ private static ArrayList uniqueControlPoints = new ArrayList();
+ static{
+ uniqueControlPoints.add(vector1);
+ uniqueControlPoints.add(vector2);
+ uniqueControlPoints.add(vector3);
+ uniqueControlPoints.add(vector4);
+ uniqueControlPoints.add(vector5);
+ uniqueControlPoints.add(vector6);
+ uniqueControlPoints.add(vector7);
+ }
+
+ private static ArrayList lineSegments = new ArrayList();
+ static{
+ lineSegments.add(new LineSegment(vector1, vector2));
+ lineSegments.add(new LineSegment(vector2, vector3));
+ lineSegments.add(new LineSegment(vector3, vector4));
+ lineSegments.add(new LineSegment(vector4, vector5));
+ lineSegments.add(new LineSegment(vector5, vector6));
+ lineSegments.add(new LineSegment(vector6, vector7));
+// lineSegments.add(new LineSegment(vector7, vector1));
+ lineSegments.add(new LineSegment(vector7, vector8));
+ };
+
+ @Test
+ public void shortenLineSegmentTest_xOnly() throws Exception
+ {
+ PVector testSegmentStart = new PVector(0, 0, 0);
+ PVector testSegmentEnd = new PVector(6, 0, 0);
+ LineSegment testSegment = new LineSegment(testSegmentStart, testSegmentEnd);
+ LineSegment shortenedSegment = OptimumIterationGenerator.shortenLineSegmentEnds(testSegment, 2.);
+ Assert.assertTrue("The shortened line segment end points aren't correct",
+ shortenedSegment.getStartPoint().equals(new PVector(2.f, 0.f, 0.f))
+ && shortenedSegment.getEndPoint().equals(new PVector(4.f, 0.f, 0.f)));
+
+ Assert.assertTrue("The shortened line segment isn't the correct length", shortenedSegment.getLength() == 2.0);
+
+ LineSegment shortenedSegment2 = OptimumIterationGenerator.shortenLineSegmentEnds(testSegment, 1.);
+ Assert.assertTrue("The shortened line segment end points aren't correct",
+ shortenedSegment2.getStartPoint().equals(new PVector(1.f, 0.f, 0.f))
+ && shortenedSegment2.getEndPoint().equals(new PVector(5.f, 0.f, 0.f)));
+
+ Assert.assertTrue("The shortened line segment isn't the correct length", shortenedSegment2.getLength() == 4.0);
+ }
+
+
+ @Test
+ public void shortenLineSegmentTest_yOnly() throws Exception
+ {
+ PVector testSegmentStart = new PVector(0, 0, 0);
+ PVector testSegmentEnd = new PVector(0, 10, 0);
+ LineSegment testSegment = new LineSegment(testSegmentStart, testSegmentEnd);
+ LineSegment shortenedSegment = OptimumIterationGenerator.shortenLineSegmentEnds(testSegment, 3.5);
+ Assert.assertTrue("The shortened line segment end points aren't correct",
+ shortenedSegment.getStartPoint().equals(new PVector(0.f, 3.5f, 0.f))
+ && shortenedSegment.getEndPoint().equals(new PVector(0.f, 6.5f, 0.f)));
+
+ Assert.assertTrue("The shortened line segment isn't the correct length", shortenedSegment.getLength() == 3.0);
+
+ LineSegment shortenedSegment2 = OptimumIterationGenerator.shortenLineSegmentEnds(testSegment, 0.);
+ Assert.assertTrue("The shortened line segment end points aren't correct",
+ shortenedSegment2.getStartPoint().equals(new PVector(0.f, 0.f, 0.f))
+ && shortenedSegment2.getEndPoint().equals(new PVector(0.f, 10.f, 0.f)));
+
+ Assert.assertTrue("The shortened line segment isn't the correct length", shortenedSegment2.getLength() == 10.0);
+ }
+
+ @Test
+ public void shortenLineSegmentTest_zOnly() throws Exception
+ {
+ PVector testSegmentStart = new PVector(0, 0, -100);
+ PVector testSegmentEnd = new PVector(0, 0, 0);
+ LineSegment testSegment = new LineSegment(testSegmentStart, testSegmentEnd);
+ LineSegment shortenedSegment = OptimumIterationGenerator.shortenLineSegmentEnds(testSegment, 15.0);
+ Assert.assertTrue("The shortened line segment end points aren't correct",
+ shortenedSegment.getStartPoint().equals(new PVector(0.f, 0.f, -85.f))
+ && shortenedSegment.getEndPoint().equals(new PVector(0.f, 0.f, -15.f)));
+
+ Assert.assertTrue("The shortened line segment isn't the correct length", shortenedSegment.getLength() == 70.0);
+
+ LineSegment shortenedSegment2 = OptimumIterationGenerator.shortenLineSegmentEnds(testSegment, 50.0);
+ Assert.assertTrue("The shortened line segment end points aren't correct",
+ shortenedSegment2.getStartPoint().equals(new PVector(0.f, 0.f, -50.f))
+ && shortenedSegment2.getEndPoint().equals(new PVector(0.f, 0.f, -50.f)));
+
+ Assert.assertTrue("The shortened line segment isn't the correct length", shortenedSegment2.getLength() == 0.0);
+ }
+
+ @Test
+ public void shortenLineSegmentTest_xyz() throws Exception
+ {
+ PVector expectedStartVector = new PVector(.758f, 1.516f, -1.0612f);
+ PVector expectedEndVector = new PVector(4.242f, 8.484f, -5.9388f);
+ double expectedLength = 9.1909;
+
+ PVector testSegmentStart = new PVector(0, 0, 0);
+ PVector testSegmentEnd = new PVector(5, 10, -7);
+ LineSegment testSegment = new LineSegment(testSegmentStart, testSegmentEnd);
+ LineSegment shortenedSegment = OptimumIterationGenerator.shortenLineSegmentEnds(testSegment, 2.0);
+ Assert.assertTrue("The shortened line segment end points aren't correct",
+ TestUtils.areVectorsEqualWithEpsilon(shortenedSegment.getStartPoint(), expectedStartVector, epsilon)
+ && TestUtils.areVectorsEqualWithEpsilon(shortenedSegment.getEndPoint(), expectedEndVector, epsilon));
+
+ Assert.assertTrue("The shortened line segment isn't the correct length",
+ shortenedSegment.getLength() + epsilon > expectedLength &&
+ shortenedSegment.getLength() - epsilon < expectedLength);
+ }
+
+ @Test
+ public void r1Test()
+ {
+ final double expectedR1 = 0.257633722007;
+ double r1 = OptimumIterationGenerator.calculateMinimumDistanceBetweenNonAdjacentSegments(lineSegments);
+ Assert.assertTrue("R1 wasn't correctly calculated", TestUtils.isEqualWithEpsilon(r1, expectedR1, epsilon));
+ }
+
+ @Test
+ public void r2Test()
+ {
+ final double expectedR2 = 0.128816861003;
+ double r1 = OptimumIterationGenerator.calculateMinimumDistanceBetweenNonAdjacentSegments(lineSegments);
+ double r2 = Math.min(r1 / 2, userEpsilon / 2); //PDZ- 1.00 is the user selected epsilon from the paper
+ Assert.assertTrue("R2 wasn't correctly calculated. R2 was: " + r2, TestUtils.isEqualWithEpsilon(r2, expectedR2, epsilon));
+ }
+
+ @Test
+ public void r3Test() throws Exception
+ {
+ final double expectedR3 = 0.0576477635811;
+ double r1 = OptimumIterationGenerator.calculateMinimumDistanceBetweenNonAdjacentSegments(lineSegments);
+ double r2 = Math.min(r1 / 2, userEpsilon / 2);
+ ArrayList shortenedCurveSegments = OptimumIterationGenerator.shortenSegments(lineSegments, r2);
+ double r3 = OptimumIterationGenerator.calculateMinimumDistanceBetweenAllSegments(shortenedCurveSegments);
+ Assert.assertTrue("R3 wasn't correctly calculated", TestUtils.isEqualWithEpsilon(r3, expectedR3, epsilon));
+ }
+
+ @Test
+ public void r4Test() throws Exception
+ {
+ final double expectedR4 = 0.00960796059685;
+ double r1 = OptimumIterationGenerator.calculateMinimumDistanceBetweenNonAdjacentSegments(lineSegments);
+ double r2 = Math.min(r1 / 2, userEpsilon/ 2);
+ ArrayList shortenedCurveSegments = OptimumIterationGenerator.shortenSegments(lineSegments, r2);
+ double r3 = OptimumIterationGenerator.calculateMinimumDistanceBetweenAllSegments(shortenedCurveSegments);
+ double r4 = r3 / 6;
+ Assert.assertTrue("R4 wasn't correctly calculated", TestUtils.isEqualWithEpsilon(r4, expectedR4, epsilon));
+ }
+
+ @Test
+ public void deltaTest() throws Exception
+ {
+ final double expectedDelta = 0.00320265353228;
+ double r1 = OptimumIterationGenerator.calculateMinimumDistanceBetweenNonAdjacentSegments(lineSegments);
+ double r2 = Math.min(r1 / 2, userEpsilon/ 2);
+ ArrayList shortenedCurveSegments = OptimumIterationGenerator.shortenSegments(lineSegments, r2);
+ double r3 = OptimumIterationGenerator.calculateMinimumDistanceBetweenAllSegments(shortenedCurveSegments);
+ double r4 = r3 / 6;
+ double delta = r4 / 3;
+ Assert.assertTrue("Delta wasn't correctly calculated", TestUtils.isEqualWithEpsilon(delta, expectedDelta, epsilon));
+ }
+
+ @Test
+ public void omegaOneTest() throws Exception
+ {
+ final double expectedOmegaOne = 86.4862;
+ double omegaOne = OptimumIterationGenerator.calculateOmegaOne(uniqueControlPoints, true);
+ Assert.assertTrue("Omega one wasn't correctly calculated", TestUtils.isEqualWithEpsilon(omegaOne, expectedOmegaOne, epsilon));
+ }
+
+ @Test
+ public void m1Test() throws Exception
+ {
+ final double expectedM1 = 29;
+ double r1 = OptimumIterationGenerator.calculateMinimumDistanceBetweenNonAdjacentSegments(lineSegments);
+ double r2 = Math.min(r1 / 2, userEpsilon/ 2);
+ ArrayList shortenedCurveSegments = OptimumIterationGenerator.shortenSegments(lineSegments, r2);
+ double r3 = OptimumIterationGenerator.calculateMinimumDistanceBetweenAllSegments(shortenedCurveSegments);
+ double r4 = r3 / 6;
+ double delta = r4 / 3;
+ double omegaOne = OptimumIterationGenerator.calculateOmegaOne(uniqueControlPoints, true);
+ int m1 = OptimumIterationGenerator.calculateM1(omegaOne, delta);
+ Assert.assertTrue("M1 wasn't correctly calculated", m1 == expectedM1);
+ }
+
+ @Test
+ public void omegaTwoTest()
+ {
+ final double expectedOmegaTwo = 975.0804;
+ final double epsilon = 0.01;
+ double omegaTwo = OptimumIterationGenerator.calculateOmegaTwo(uniqueControlPoints);
+ Assert.assertTrue("Omega two wasn't correctly calculated", TestUtils.isEqualWithEpsilon(omegaTwo, expectedOmegaTwo, epsilon));
+ }
+
+ @Test
+ public void lambdaTest()
+ {
+ final double expectedLambda = 9.12030317807473;
+ double lambda = OptimumIterationGenerator.calculateLambda(lineSegments);
+ Assert.assertTrue("Lambda wasn't correctly calculated", TestUtils.isEqualWithEpsilon(lambda, expectedLambda, epsilon));
+ }
+
+ @Test
+ public void m2Test()
+ {
+ int expectedM2 = 4;
+ double omegaTwo = OptimumIterationGenerator.calculateOmegaTwo(knotPoints);
+ double lambda = OptimumIterationGenerator.calculateLambda(lineSegments);
+ int m2 = OptimumIterationGenerator.calculateM2(lineSegments, omegaTwo, lambda);
+ Assert.assertTrue("M2 wasn't correctly calculated", m2 == expectedM2);
+ }
+
+ @Test
+ public void m3Test()
+ {
+ int expectedM3 = 5;
+ double omegaTwo = OptimumIterationGenerator.calculateOmegaTwo(knotPoints);
+ double lambda = OptimumIterationGenerator.calculateLambda(lineSegments);
+ int m3 = OptimumIterationGenerator.calculateM3(lineSegments, omegaTwo, lambda);
+ Assert.assertTrue("M3 wasn't correctly calculated", m3 == expectedM3);
+ }
+}
\ No newline at end of file
diff --git a/junit_tests/com/knotrenderer/utiltest/TestUtils.java b/junit_tests/com/knotrenderer/utiltest/TestUtils.java
new file mode 100644
index 0000000..d92c56b
--- /dev/null
+++ b/junit_tests/com/knotrenderer/utiltest/TestUtils.java
@@ -0,0 +1,30 @@
+package com.knotrenderer.utiltest;
+
+import processing.core.PVector;
+
+/**
+ * A utility class for tests. This contains methods which allows for easier testing and less
+ * duplicate/redundant code.
+ * @author Peter
+ *
+ */
+public class TestUtils
+{
+ public static boolean areVectorsExactlyEqual(PVector vector1, PVector vector2)
+ {
+ return areVectorsEqualWithEpsilon(vector1, vector2, 0.0);
+ }
+
+ public static boolean areVectorsEqualWithEpsilon(PVector vector1, PVector vector2, double epsilon)
+ {
+ return isEqualWithEpsilon(vector1.x, vector2.x, epsilon)
+ && isEqualWithEpsilon(vector1.y, vector2.y, epsilon)
+ && isEqualWithEpsilon(vector1.z, vector2.z, epsilon);
+ }
+
+ public static boolean isEqualWithEpsilon(double value1, double value2, double epsilon)
+ {
+ return value1 + epsilon > value2 && value1 - epsilon < value2;
+ }
+
+}
diff --git a/junit_tests/com/knotrenderer/utiltest/UnboxUtilsTest.java b/junit_tests/com/knotrenderer/utiltest/UnboxUtilsTest.java
new file mode 100644
index 0000000..49d3f4a
--- /dev/null
+++ b/junit_tests/com/knotrenderer/utiltest/UnboxUtilsTest.java
@@ -0,0 +1,14 @@
+package com.knotrenderer.utiltest;
+
+import static org.junit.Assert.*;
+
+import org.junit.Test;
+
+public class UnboxUtilsTest
+{
+ @Test
+ public static void unboxDoublesFromArrayList()
+ {
+
+ }
+}
diff --git a/libs/controlP5.jar b/libs/controlP5.jar
new file mode 100644
index 0000000..eb84880
Binary files /dev/null and b/libs/controlP5.jar differ
diff --git a/libs/core.jar b/libs/core.jar
new file mode 100644
index 0000000..99f0ba3
Binary files /dev/null and b/libs/core.jar differ
diff --git a/libs/export.txt b/libs/export.txt
new file mode 100644
index 0000000..88d0bc2
--- /dev/null
+++ b/libs/export.txt
@@ -0,0 +1,11 @@
+# If you want to support more platforms, visit jogamp.org to get the
+# natives libraries for the platform in question (i.e. Solaris).
+
+name = OpenGL
+
+application.macosx=core.jar,jogl-all.jar,gluegen-rt.jar,jogl-all-natives-macosx-universal.jar,gluegen-rt-natives-macosx-universal.jar
+application.windows32=core.jar,jogl-all.jar,gluegen-rt.jar,jogl-all-natives-windows-i586.jar,gluegen-rt-natives-windows-i586.jar
+application.windows64=core.jar,jogl-all.jar,gluegen-rt.jar,jogl-all-natives-windows-amd64.jar,gluegen-rt-natives-windows-amd64.jar
+application.linux32=core.jar,jogl-all.jar,gluegen-rt.jar,jogl-all-natives-linux-i586.jar,gluegen-rt-natives-linux-i586.jar
+application.linux64=core.jar,jogl-all.jar,gluegen-rt.jar,jogl-all-natives-linux-amd64.jar,gluegen-rt-natives-linux-amd64.jar
+application.linux-armv6hf=core.jar,jogl-all.jar,gluegen-rt.jar,jogl-all-natives-linux-armv6hf.jar,gluegen-rt-natives-linux-armv6hf.jar
\ No newline at end of file
diff --git a/libs/gluegen-rt-natives-linux-amd64.jar b/libs/gluegen-rt-natives-linux-amd64.jar
new file mode 100644
index 0000000..a2466f4
Binary files /dev/null and b/libs/gluegen-rt-natives-linux-amd64.jar differ
diff --git a/libs/gluegen-rt-natives-linux-armv6hf.jar b/libs/gluegen-rt-natives-linux-armv6hf.jar
new file mode 100644
index 0000000..5ef0673
Binary files /dev/null and b/libs/gluegen-rt-natives-linux-armv6hf.jar differ
diff --git a/libs/gluegen-rt-natives-linux-i586.jar b/libs/gluegen-rt-natives-linux-i586.jar
new file mode 100644
index 0000000..914a259
Binary files /dev/null and b/libs/gluegen-rt-natives-linux-i586.jar differ
diff --git a/libs/gluegen-rt-natives-macosx-universal.jar b/libs/gluegen-rt-natives-macosx-universal.jar
new file mode 100644
index 0000000..15df5e8
Binary files /dev/null and b/libs/gluegen-rt-natives-macosx-universal.jar differ
diff --git a/libs/gluegen-rt-natives-windows-amd64.jar b/libs/gluegen-rt-natives-windows-amd64.jar
new file mode 100644
index 0000000..517fb84
Binary files /dev/null and b/libs/gluegen-rt-natives-windows-amd64.jar differ
diff --git a/libs/gluegen-rt-natives-windows-i586.jar b/libs/gluegen-rt-natives-windows-i586.jar
new file mode 100644
index 0000000..1c393b7
Binary files /dev/null and b/libs/gluegen-rt-natives-windows-i586.jar differ
diff --git a/libs/gluegen-rt.jar b/libs/gluegen-rt.jar
new file mode 100644
index 0000000..742fdb2
Binary files /dev/null and b/libs/gluegen-rt.jar differ
diff --git a/libs/jogl-all-natives-linux-amd64.jar b/libs/jogl-all-natives-linux-amd64.jar
new file mode 100644
index 0000000..e57b8c7
Binary files /dev/null and b/libs/jogl-all-natives-linux-amd64.jar differ
diff --git a/libs/jogl-all-natives-linux-armv6hf.jar b/libs/jogl-all-natives-linux-armv6hf.jar
new file mode 100644
index 0000000..5aea1b2
Binary files /dev/null and b/libs/jogl-all-natives-linux-armv6hf.jar differ
diff --git a/libs/jogl-all-natives-linux-i586.jar b/libs/jogl-all-natives-linux-i586.jar
new file mode 100644
index 0000000..88a27ce
Binary files /dev/null and b/libs/jogl-all-natives-linux-i586.jar differ
diff --git a/libs/jogl-all-natives-macosx-universal.jar b/libs/jogl-all-natives-macosx-universal.jar
new file mode 100644
index 0000000..c65ea99
Binary files /dev/null and b/libs/jogl-all-natives-macosx-universal.jar differ
diff --git a/libs/jogl-all-natives-windows-amd64.jar b/libs/jogl-all-natives-windows-amd64.jar
new file mode 100644
index 0000000..9577bf1
Binary files /dev/null and b/libs/jogl-all-natives-windows-amd64.jar differ
diff --git a/libs/jogl-all-natives-windows-i586.jar b/libs/jogl-all-natives-windows-i586.jar
new file mode 100644
index 0000000..4439f1d
Binary files /dev/null and b/libs/jogl-all-natives-windows-i586.jar differ
diff --git a/libs/jogl-all.jar b/libs/jogl-all.jar
new file mode 100644
index 0000000..e2c7b44
Binary files /dev/null and b/libs/jogl-all.jar differ
diff --git a/libs/junit.jar b/libs/junit.jar
new file mode 100644
index 0000000..3a7fc26
Binary files /dev/null and b/libs/junit.jar differ
diff --git a/libs/org.hamcrest.core_1.3.0.v201303031735.jar b/libs/org.hamcrest.core_1.3.0.v201303031735.jar
new file mode 100644
index 0000000..0adbff1
Binary files /dev/null and b/libs/org.hamcrest.core_1.3.0.v201303031735.jar differ
diff --git a/src/com/knotrenderer/model/BezierCurve.java b/src/com/knotrenderer/model/BezierCurve.java
new file mode 100644
index 0000000..5fbbc67
--- /dev/null
+++ b/src/com/knotrenderer/model/BezierCurve.java
@@ -0,0 +1,104 @@
+package com.knotrenderer.model;
+
+import java.math.BigInteger;
+import java.util.ArrayList;
+
+import com.knotrenderer.util.MathUtil;
+
+import processing.core.PVector;
+
+public class BezierCurve
+{
+ private final PVector[] controlPoints;
+ private final PVector[] coefficients;
+
+ public BezierCurve(ArrayList controlPoints)
+ {
+ this(controlPoints.toArray(new PVector[controlPoints.size()]));
+ }
+
+ public BezierCurve(PVector[] controlPoints)
+ {
+ this.controlPoints = controlPoints;
+ coefficients = new PVector[controlPoints.length];
+ generateBezierCoefficients();
+ }
+
+ private void generateBezierCoefficients()
+ {
+ for(int k = 0; k < controlPoints.length; k++)
+ {
+ PVector vector = controlPoints[k].copy();
+ coefficients[k] = vector.mult((float) MathUtil.binomialCoefficientAsDouble(controlPoints.length-1, k));
+ System.out.println(coefficients[k]);
+ }
+ }
+
+ public PVector getCurveValueFromSummationExpansion(double t)
+ {
+ double n = coefficients.length;
+ double initialOneMinusTFactor = Math.pow((1 - t), n);
+ PVector value = coefficients[0].copy().mult((float) initialOneMinusTFactor);
+
+ /*
+ * PDZ- We only want to go to the second to last point (hence coefficient.length - 1) because the last term will be zero if the
+ * (1-t)^n-i is left in. It is undefined and will ruin the value if we leave it.
+ */
+
+ for(int i = 1; i < coefficients.length-1; i++)
+ {
+ double parametricFactor = Math.pow((1 - t), n-i) * Math.pow(t, i);
+ PVector partialSum = coefficients[i].copy().mult((float)parametricFactor);
+ value.add(partialSum);
+ }
+
+ //PDZ- finally, add the product from the last (or end) term (t^n * PN)
+ double finalOneMinusTFactor = Math.pow(t, n);
+ PVector endControlPointProduct = coefficients[coefficients.length-1].copy().mult((float)finalOneMinusTFactor);
+ value.add(endControlPointProduct);
+ return value;
+ }
+
+ /**
+ * Given a parametrized point (between 0 and 1) return the value of the Bezier curve at that point.
+ * Since the Bezier curve is represented in a parametric form spanning from 0 to 1 you must
+ * provide a point in the range.
+ * @param parametrizedPoint
+ * @return {@link PVector} - point on the Bezier curve at the parametrized value
+ */
+ public PVector getCurveValueRecursively(double parametrizedPoint)
+ {
+ return getCurveValueRecursively(parametrizedPoint, 0, controlPoints.length - 1);
+ }
+
+ /**
+ * Given a parametrized point (between 0 and 1) return the value of the Bezier curve at that point.
+ * Since the Bezier curve is represented in a parametric form spanning from 0 to 1 you must
+ * provide a point in the range.
+ * @param parametrizedPoint
+ * @param numberOfControlPoints
+ * @return {@link PVector} - point on the Bezier curve at the parametrized value
+ */
+ public PVector getCurveValueRecursively(double parametrizedPoint, int leftControlPointIndex, int rightControlPointIndex)
+ {
+ /**
+ * TODO: PDZ- A very nice to have would be to load Bezier curves from a file. Since
+ * expanding the entire curve out in it's explicit form is going to get expensive,
+ * especially if it is calculated every single time, it would be good to have a file
+ * which stores the expanded form. Given the number of points on the Bezier curve it
+ * should check to see if a generated file (or line in a file) is already in existence.
+ * Otherwise, it should generate/add the calculated value.
+ */
+ if(parametrizedPoint < 0 || parametrizedPoint > 1)
+ {
+ return null;
+ }
+ if(leftControlPointIndex == rightControlPointIndex)
+ {
+ return controlPoints[leftControlPointIndex].copy();
+ }
+ PVector leftRecurseBranch = getCurveValueRecursively(parametrizedPoint, leftControlPointIndex, rightControlPointIndex - 1).mult((float)(1 - parametrizedPoint));
+ PVector rightRecurseBranch = getCurveValueRecursively(parametrizedPoint, leftControlPointIndex + 1, rightControlPointIndex).mult((float)parametrizedPoint);
+ return leftRecurseBranch.add(rightRecurseBranch);
+ }
+}
diff --git a/src/com/knotrenderer/model/Curve.java b/src/com/knotrenderer/model/Curve.java
new file mode 100644
index 0000000..abf9d67
--- /dev/null
+++ b/src/com/knotrenderer/model/Curve.java
@@ -0,0 +1,241 @@
+package com.knotrenderer.model;
+
+import java.io.IOException;
+import java.nio.charset.Charset;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Collections;
+
+import com.knotrenderer.util.OptimumIterationGenerator;
+
+import processing.core.PVector;
+
+public class Curve
+{
+ private final String curvePath;
+
+ /**
+ * PDZ- knotPoints is the set of points from the curve file- this file has the start and end point the same since
+ * for a closed Bezier curve the start and point are the same.
+ */
+ private final ArrayList knotPoints;
+ /**
+ * PDZ- uniqueControlPoints is the set of UNIQUE control points for the Bezier curve. This remove the end point
+ * of the knotPoints set since that will mess up some calculations (like the closest distance to segment when generating
+ * the OptimumIterations)
+ */
+ private final ArrayList uniqueControlPoints;
+
+ private final ArrayList lineSegments;
+ private final double epsilon = 1.0;
+ private final int[] optimumIterations;
+
+ public Curve(ArrayList knotPoints, String curvePath)
+ {
+ this.curvePath = curvePath;
+ this.knotPoints = knotPoints;
+ uniqueControlPoints = new ArrayList(knotPoints.size());
+
+ /**
+ * PDZ- Copy the knot points to the uniqueControlPoints and remove the duplicate control point.
+ * Since the Bezier curve's start and end point in the file are the same (this is because the curve is closed)
+ * we need to remove the last point since it is a duplicate. The duplicity causes issues when calculating closest
+ * distances and Omega Two.
+ */
+ for(PVector vector : knotPoints)
+ {
+ uniqueControlPoints.add(vector.copy());
+ }
+ /**
+ * PDZ- Check to see if the curve is closed. If it isn't, report it to the user
+ */
+ if(uniqueControlPoints.get(0).equals(uniqueControlPoints.get(uniqueControlPoints.size()-1)))
+ {
+ uniqueControlPoints.remove(uniqueControlPoints.size()-1);
+ }
+ else
+ {
+ System.out.println("Curve isn't closed");
+ }
+
+ this.lineSegments = new ArrayList();
+ generateLineSegments();
+ optimumIterations = calculateOptimumIterations();
+ }
+
+ /**
+ * Returns the knot points of the curve. This returns a new instance of an {@link ArrayList} which
+ * contains new instances of every {@link PVector} in {@link #knotPoints} since {@link PVector}s
+ * modify the original object whenever vector arithmetic is applied to the {@link PVector}
+ * @return
+ */
+ public ArrayList getKnotPoints()
+ {
+ ArrayList points = new ArrayList();
+ for(PVector vector : knotPoints)
+ {
+ points.add(vector.copy());
+ }
+ return points;
+ }
+
+ /**
+ * @return int[3] - the three M values from Professor Peter's and Ji Li's paper.
+ */
+ public int[] getOptimumIterations()
+ {
+ return optimumIterations;
+ }
+
+ /**
+ *
+ * @return int - M1 which was generated from the algorithm in Professor Peters' and Ji Li's paper.
+ */
+ public int getM1()
+ {
+ return optimumIterations[0];
+ }
+
+ /**
+ *
+ * @return int - M1 which was generated from the algorithm in Professor Peters' and Ji Li's paper.
+ */
+ public int getM2()
+ {
+ return optimumIterations[1];
+ }
+
+ /**
+ *
+ * @return int - M1 which was generated from the algorithm in Professor Peters' and Ji Li's paper.
+ */
+ public int getM3()
+ {
+ return optimumIterations[2];
+ }
+
+ /**
+ * Generates the line segments from the points in the curve. The line segments consist of consecutive points. For
+ * example, (p1, p2), (p2, p3), ... are line segments where p1, p2, etc. are points.
+ */
+ private void generateLineSegments()
+ {
+ int numPoints = knotPoints.size();
+ for(int i = 0; i < numPoints - 1; i++)
+ {
+ // if(i == (numPoints - 1))
+ // {
+ // lineSegments.add(new LineSegment(knotPoints.get(i), knotPoints.get(0))); //Close off the curve
+ // }
+ // else
+ // {
+ // lineSegments.add(new LineSegment(knotPoints.get(i), knotPoints.get(i + 1)));
+ // }
+ lineSegments.add(new LineSegment(knotPoints.get(i), knotPoints.get(i + 1)));
+ }
+ }
+
+ private int[] calculateOptimumIterations()
+ {
+ /**
+ * r1, r2, r3, r4, delta, are from the Denne-Sullivan paper;
+ * omega1, omega2, lambda, m1, m2, m3 are all from the Peters' paper
+ */
+ double r1 = OptimumIterationGenerator.calculateMinimumDistanceBetweenNonAdjacentSegments(lineSegments);
+ System.out.println("r1 is: " + r1);
+
+ double r2 = Math.min(r1 / 2, epsilon / 2);
+ System.out.println("r2 is: " + r2);
+
+ double r3 = -1; //This shouldn't ever remain -1 unless there is an exception below
+ try
+ {
+ ArrayList shortenedCurveSegments = OptimumIterationGenerator.shortenSegments(lineSegments, r2);
+ r3 = OptimumIterationGenerator.calculateMinimumDistanceBetweenAllSegments(shortenedCurveSegments);
+ }
+ catch(Exception e)
+ {
+ System.out.println(e.getMessage());
+ e.printStackTrace();
+ }
+ System.out.println("r3 is: " + r3);
+
+ double r4 = r3 / 6;
+ System.out.println("r4 is: " + r4);
+
+ double delta = r4 / 3;
+ System.out.println("delta is: " + delta);
+
+ double omegaOne = OptimumIterationGenerator.calculateOmegaOne(this.uniqueControlPoints, true);
+ System.out.println("omega one is: " + omegaOne);
+
+ int m1 = OptimumIterationGenerator.calculateM1(omegaOne, delta);
+ System.out.println("m1 is: " + m1);
+
+ double omegaTwo = OptimumIterationGenerator.calculateOmegaTwo(this.uniqueControlPoints);
+ System.out.println("omega two is: " + omegaTwo);
+
+ double lambda = OptimumIterationGenerator.calculateLambda(lineSegments);
+ System.out.println("lambda is: " + lambda);
+
+ int m2 = OptimumIterationGenerator.calculateM2(lineSegments, omegaTwo, lambda);
+ System.out.println("m2 is: " + m2);
+
+ int m3 = OptimumIterationGenerator.calculateM3(lineSegments, omegaTwo, lambda);
+ System.out.println("m3 is: " + m3);
+
+ return new int[]{m1, m2, m3};
+ }
+
+ /**
+ * Generates the next iteration of the curve. This is done by taking each line segment
+ * and finding the midpoint of the segment. The new curve consists of all the curve points
+ * from the original curve plus the midpoints that were generated.
+ * @return
+ */
+ public Curve generateNextSubdivision()
+ {
+ final ArrayList pointsToWrite = new ArrayList();
+ final ArrayList subdivisionPoints = new ArrayList();
+
+ String[] curveSplit = curvePath.split(".curve");
+ String newPath = curveSplit[0] + "_TEST.curve";
+ Path filePath = Paths.get(newPath);
+
+ /**
+ * PDZ- Iterate through all the points and find the midpoint.
+ * Don't add the end point since on the next iteration of the loop
+ * it becomes the start point and we add it then. This works since if we didn't
+ * do this we would have one point twice which would mess up shortening the segments later.
+ */
+ for(int i = 0; i < knotPoints.size(); i++)
+ {
+ final PVector startPoint = knotPoints.get(i);
+ final PVector endPoint = knotPoints.get((i + 1) % knotPoints.size());
+ float midPointX = (startPoint.x + endPoint.x) / 2;
+ float midPointY = (startPoint.y + endPoint.y) / 2;
+ float midPointZ = (startPoint.z + endPoint.z) / 2;
+ PVector midPoint = new PVector(midPointX, midPointY, midPointZ);
+
+ subdivisionPoints.add(startPoint);
+ subdivisionPoints.add(midPoint);
+
+ String startPointString = startPoint.x + ", " + startPoint.y + ", " + startPoint.z + "\n";
+ String midPointString = midPoint.x + ", " + midPoint.y + ", " + midPoint.z + "\n";
+ pointsToWrite.add(startPointString);
+ pointsToWrite.add(midPointString);
+ }
+
+ try {
+ Files.write(filePath, pointsToWrite, Charset.forName("UTF-8"));
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+
+ return new Curve(subdivisionPoints, newPath);
+ }
+
+
+}
diff --git a/src/com/knotrenderer/model/LineSegment.java b/src/com/knotrenderer/model/LineSegment.java
new file mode 100644
index 0000000..4dcdaf3
--- /dev/null
+++ b/src/com/knotrenderer/model/LineSegment.java
@@ -0,0 +1,200 @@
+package com.knotrenderer.model;
+
+import com.knotrenderer.util.MathUtil;
+import processing.core.PMatrix3D;
+import processing.core.PVector;
+
+public class LineSegment
+{
+ private final PVector startPoint;
+ private final PVector endPoint;
+ private final double length;
+
+ public LineSegment(PVector startPoint, PVector endPoint)
+ {
+ this.startPoint = startPoint;
+ this.endPoint = endPoint;
+ this.length = calculateLength();
+ }
+
+ /**
+ * A special private constructor which returns a nulled out line segment with a length. This
+ * is used when two lines are being compared for the closest distance between the two and the
+ * segments are found to be parallel. In such a case there are infinite lines that can be
+ * taken between the lines but all having the same distance.
+ * @param distance
+ */
+ private LineSegment(double distance)
+ {
+ startPoint = null;
+ endPoint = null;
+ this.length = distance;
+ }
+
+ /**
+ * @return a copy of the start point segment. Since all operations on PVectors will modify the original
+ * we want to make sure we send a copy so that this instance isn't modified
+ */
+ public PVector getStartPoint()
+ {
+ return this.startPoint.copy();
+ }
+
+ /**
+ * @return a copy of the end point segment. Since all operations on PVectors will modify the original
+ * we want to make sure we send a copy so that this instance isn't modified
+ */
+ public PVector getEndPoint()
+ {
+ return this.endPoint.copy();
+ }
+
+ public double getLength()
+ {
+ return this.length;
+ }
+
+ public LineSegment closestDistanceToLineSegment(LineSegment segment)
+ {
+ return this.closestDistanceToLineSegment(segment, true);
+ }
+
+ public LineSegment closestDistanceToLineSegment(LineSegment segment, boolean clampAll)
+ {
+ return this.closestDistanceToLineSegment(segment, true, true, true, true);
+ }
+
+ public LineSegment closestDistanceToLineSegment(LineSegment segment, boolean clampThisSegmentStart, boolean clampThisSegmentEnd, boolean clampComparingSegmentStart, boolean clampComparingSegmentEnd)
+ {
+ /*
+ * PDZ - This comes from the Python version in 3d_line_intersection_test.py (not the best name since it does more than that) but
+ *
+ */
+ //Renamed for convenience
+ LineSegment segmentA = this; //A0 and A1
+ LineSegment segmentB = segment; //B0 and B1
+
+ PVector segAStartPoint = segmentA.getStartPoint(); //A0
+ PVector segAEndPoint = segmentA.getEndPoint(); //A1
+ PVector segBStartPoint = segmentB.getStartPoint(); //B0
+ PVector segBEndPoint = segmentB.getEndPoint(); //B1
+
+ boolean clampThisSegStart = clampThisSegmentStart; //clampA0
+ boolean clampThisSegEnd = clampThisSegmentEnd; //clampA1
+ boolean clampComparingSegStart = clampComparingSegmentStart; //clampB0
+ boolean clampComparingSegEnd = clampComparingSegmentEnd; //clampB1
+
+ //Calculate the denominator
+ PVector segADenom = segAEndPoint.copy().sub(segAStartPoint); //A
+ PVector segBDenom = segBEndPoint.copy().sub(segBStartPoint); //B
+
+ float normA = (float) MathUtil.calculateL2Norm(segADenom);
+ float normB = (float) MathUtil.calculateL2Norm(segBDenom);
+ PVector segANormalized = segADenom.copy().normalize(); //_A
+ PVector segBNormalized = segBDenom.copy().normalize(); //_B
+
+ PVector crossProduct = segANormalized.cross(segBNormalized);
+
+ double denom = Math.pow(MathUtil.calculateL2Norm(crossProduct), 2);
+
+ //lines are parallel so we need to calculate the distance with a projection and evaluate clamp edge cases
+ if(denom < 0.0000001) //Since it is a double you can't check == 0
+ {
+ float d0 = segANormalized.dot(segBStartPoint.sub(segAStartPoint));
+ PVector d = (segANormalized.copy().mult(d0).add(segAStartPoint)).sub(segBStartPoint);
+ double dNormalized = MathUtil.calculateL2Norm(d);
+
+ //If clamping: the only time the points will have a distance is if they aren't the same line - check to see if they overlap (on the line) using dot product
+ if(clampThisSegStart || clampThisSegEnd || clampComparingSegStart || clampComparingSegEnd)
+ {
+ PVector bEndDiffAStartVector = segBEndPoint.copy().sub(segAStartPoint);
+ double dist1 = segANormalized.dot(bEndDiffAStartVector);
+
+ //Check to see if segment B is before segment A
+ if(d0 <= 0.0000001 && 0.0000001 >= dist1)
+ {
+ if(clampThisSegStart && clampComparingSegEnd)
+ {
+ if(Math.abs(d0) < Math.abs(dist1))
+ {
+ return new LineSegment(segBStartPoint, segAStartPoint);
+ }
+ return new LineSegment(segBEndPoint, segAStartPoint);
+ }
+ }
+ //Check to see if segment B is after A
+ else if(d0 >= normA && normA <= dist1)
+ {
+ if(clampThisSegEnd && clampComparingSegStart)
+ {
+ if(Math.abs(d0) < Math.abs(dist1))
+ {
+ return new LineSegment(segBStartPoint, segAEndPoint);
+ }
+ return new LineSegment(segBEndPoint, segAEndPoint);
+ }
+ }
+ }
+ //Otherwise, the segments are overlapping (or clamping is off) so just return the distance 0 since there are an infinite amount of points that could be closest
+ //This happens when they are parallel or the same line
+ return new LineSegment(dNormalized);
+ }
+
+ //Else - lines are crossing: calculate the determinant and return the segment
+
+ PVector t = segBStartPoint.copy().sub(segAStartPoint);
+ final float IGNORED_VALUE = 0;
+ PMatrix3D normalizedBMatrix = new PMatrix3D(t.x, segBNormalized.x, crossProduct.x, IGNORED_VALUE,
+ t.y, segBNormalized.y, crossProduct.y, IGNORED_VALUE,
+ t.z, segBNormalized.z, crossProduct.z, IGNORED_VALUE,
+ IGNORED_VALUE, IGNORED_VALUE, IGNORED_VALUE, IGNORED_VALUE);
+
+ PMatrix3D normalizedAMatrix = new PMatrix3D(t.x, segANormalized.x, crossProduct.x, IGNORED_VALUE,
+ t.y, segANormalized.y, crossProduct.y, IGNORED_VALUE,
+ t.z, segANormalized.z, crossProduct.z, IGNORED_VALUE,
+ IGNORED_VALUE, IGNORED_VALUE, IGNORED_VALUE, IGNORED_VALUE);
+
+ float det0 = (float) MathUtil.calculate3x3Determinant(normalizedBMatrix);
+ float det1 = (float) MathUtil.calculate3x3Determinant(normalizedAMatrix);
+
+ float t0 = (float) (det0 / denom);
+ float t1 = (float) (det1 / denom);
+
+ PVector pointA = segAStartPoint.copy().add(segANormalized.mult(t0));
+ PVector pointB = segBStartPoint.copy().add(segBNormalized.mult(t1));
+
+ //Clamp if needed:
+ if(clampThisSegStart || clampThisSegEnd | clampComparingSegEnd || clampComparingSegEnd)
+ {
+ if(t0 < 0 && clampThisSegStart)
+ {
+ pointA = segAStartPoint;
+ }
+ else if(t0 > normA && clampThisSegEnd)
+ {
+ pointA = segAEndPoint;
+ }
+
+ if(t1 < 0 && clampComparingSegStart)
+ {
+ pointB = segBStartPoint;
+ }
+ else if(t1 > normB && clampComparingSegEnd)
+ {
+ pointB = segBEndPoint;
+ }
+ }
+
+// PVector diffpApB = pointA.copy().sub(pointB);
+// double distance = MathUtil.calculateL2Norm(diffpApB);
+// System.out.println("The closest distance is: " + distance);
+
+ return new LineSegment(pointA, pointB);
+ }
+
+ private double calculateLength()
+ {
+ PVector diff = startPoint.copy().sub(endPoint);
+ return MathUtil.calculateL2Norm(diff);
+ }
+}
diff --git a/src/com/knotrenderer/util/CurveLoader.java b/src/com/knotrenderer/util/CurveLoader.java
new file mode 100644
index 0000000..b7cdca7
--- /dev/null
+++ b/src/com/knotrenderer/util/CurveLoader.java
@@ -0,0 +1,59 @@
+package com.knotrenderer.util;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.util.ArrayList;
+
+import com.knotrenderer.model.Curve;
+
+import processing.core.PVector;
+
+public class CurveLoader
+{
+ public static Curve loadCurveFromFile(String path)
+ {
+ File file = new File(path);
+ ArrayList knotPoints = new ArrayList();
+
+ try{
+ BufferedReader bufferedReader = new BufferedReader(new FileReader(file));
+ String line;
+ while ((line = bufferedReader.readLine()) != null)
+ {
+ if(line.equals("") || line.replaceAll("\\s+", "").startsWith("%"))
+ continue;
+ else
+ {
+ //TODO: handle the case where you have a comment at the end of the line
+ line = line.replaceAll("\\s+", "");
+ String[] splitString = line.split(",");
+
+ //TODO: handle when a line has more or less than 3 coordinates on it (meaning display to the user when it isn't correct)
+ if(splitString.length != 3)
+ {
+ throw new Exception("File line wasn't the correct lenght. It should have exactly 3 numbers on it.");
+ }
+ else
+ {
+ //TODO: Figure out why there is some precision loss here and how to fix it (compare file to values read in)
+ float x = Float.parseFloat(splitString[0]);
+ float y = Float.parseFloat(splitString[1]);
+ float z = Float.parseFloat(splitString[2]);
+
+ System.out.println("X is: " + x + " Y is: " + y + " Z is: " + z);
+ knotPoints.add(new PVector(x, y, z));
+ }
+
+ }
+ }
+ bufferedReader.close();
+ }
+ catch(Exception e)
+ {
+ e.printStackTrace();
+ }
+
+ return new Curve(knotPoints, path);
+ }
+}
diff --git a/src/com/knotrenderer/util/MathUtil.java b/src/com/knotrenderer/util/MathUtil.java
new file mode 100644
index 0000000..bb5553a
--- /dev/null
+++ b/src/com/knotrenderer/util/MathUtil.java
@@ -0,0 +1,115 @@
+package com.knotrenderer.util;
+
+import java.math.BigInteger;
+import java.util.Arrays;
+
+import processing.core.PMatrix3D;
+import processing.core.PVector;
+
+public class MathUtil
+{
+ /**
+ * Calculates the l1 norm of the given values. The l1 norm is
+ * the sum of the absolute values of the given values
+ * @param values
+ * @return double - the l1 norm value
+ */
+ public static double calculateL1Norm(double...values)
+ {
+ double l1Norm = 0;
+ for(double value : values)
+ {
+ l1Norm += Math.abs(value);
+ }
+ return l1Norm;
+ }
+
+ /**
+ * Calculates the l2 norm of the given vector. The l2 norm is the
+ * square root of the sum of the squares of the x, y, and z coordinates.
+ * @param vector
+ * @return double - the l2 norm value
+ */
+ public static double calculateL2Norm(PVector vector)
+ {
+ return MathUtil.calculateL2Norm(vector.x, vector.y, vector.z);
+ }
+
+
+ /**
+ * Calculates the l2 norm. l2 norm is the square root of the sum
+ * of all values squared.
+ * @param values
+ * @return the l2 norm from the values
+ */
+ public static double calculateL2Norm(double...values)
+ {
+ double squaredSum = 0;
+
+ for(double value : values)
+ {
+ squaredSum += value * value;
+ }
+ return Math.sqrt(squaredSum);
+ }
+
+ /**
+ * Matrix looks like this:
+ * | a b c ? |
+ * | d e f ? |
+ * | g h i ? |
+ * | ? ? ? ? |
+ * since {@link PMatrix3D} is a 4 x 4. We are expecting a 3 x 3 in the format above where
+ * everything with a (?) will not be used. This function will return the determinant of the
+ * inner 3x3 matrix.
+ * @param matrix
+ * @return double determinant of the matrix
+ */
+ public static double calculate3x3Determinant(PMatrix3D matrix)
+ {
+ double a = matrix.m00;
+ double b = matrix.m01;
+ double c = matrix.m02;
+ double d = matrix.m10;
+ double e = matrix.m11;
+ double f = matrix.m12;
+ double g = matrix.m20;
+ double h = matrix.m21;
+ double i = matrix.m22;
+
+ double firstSubDeterminant = a * ((e * i) - (f * h));
+ double secondSubDeterminant = b * ((d * i) - (f * g));
+ double thirdSubDeterminant = c * ((d * h) - (e * g));
+
+ return firstSubDeterminant - secondSubDeterminant + thirdSubDeterminant;
+ }
+
+ /**
+ * Returns N Choose K or the binomial coefficient as a double
+ * @param N
+ * @param K
+ * @return double - the binomial coefficient of N choose K
+ */
+ public static double binomialCoefficientAsDouble(final int N, final int K)
+ {
+ if(K == 0 || K == N)
+ {
+ return 1;
+ }
+
+ double nChooseK = 1;
+ for (int k = 0; k < K; k++) {
+ nChooseK = nChooseK * (N-k) / (k+1);
+ }
+ return nChooseK;
+ }
+
+// static BigInteger binomial(final int N, final int K) {
+// BigInteger ret = BigInteger.ONE;
+// for (int k = 0; k < K; k++) {
+// ret = ret.multiply(BigInteger.valueOf(N-k))
+// .divide(BigInteger.valueOf(k+1));
+// }
+// return ret;
+// }
+}
diff --git a/src/com/knotrenderer/util/OptimumIterationGenerator.java b/src/com/knotrenderer/util/OptimumIterationGenerator.java
new file mode 100644
index 0000000..14dba31
--- /dev/null
+++ b/src/com/knotrenderer/util/OptimumIterationGenerator.java
@@ -0,0 +1,330 @@
+package com.knotrenderer.util;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Comparator;
+
+import com.knotrenderer.model.LineSegment;
+
+import processing.core.PVector;
+
+/**
+ * PDZ - This class is used to calculate the optimum number of subdivisions (iterations) of the stick
+ * knot (control polygon for the Bezier curve). It comes directly from Peters' paper.
+ *
+ */
+public class OptimumIterationGenerator
+{
+
+ /**
+ * Calculates the minimum distance between all non-adjacent line segments. It will compare every line
+ * segment to all other non-adjacent line segments and return the minimum of all the comparisons
+ * @param lineSegments
+ * @return {@link Double} the minimum of all the comparisons
+ */
+ public static Double calculateMinimumDistanceBetweenNonAdjacentSegments(ArrayList lineSegments)
+ {
+ Double[] distances = calculateDistancesBetweenNonAdjacentSegments(lineSegments);
+ Arrays.sort(distances); //Sorts in increasing order
+ return distances[0];
+ }
+
+ /**
+ * Given a list of line segments, return the array of distances between every segment compared to all other
+ * NON-ADJACENT segments in the list. Adjacent segments are the previous and next segments in the array list
+ * input.
+ * @param lineSegments
+ * @return array of Doubles of the calculated distances
+ */
+ public static Double[] calculateDistancesBetweenNonAdjacentSegments(ArrayList lineSegments)
+ {
+ //Each segment between two points (aka control points of the Bezier curve) on the curve determines the drawn knot (Bezier polygon)
+ int numControlPointSegments = lineSegments.size();
+ ArrayList distances = new ArrayList();
+
+ for(int i = 0; i < numControlPointSegments; i++) //For all the line segments
+ {
+ //Compare them to all NON_ADJACENT (hence j = i + 2) segments
+ int j = i + 2;
+ while(j < numControlPointSegments)
+ {
+ //The first line segment and the last are adjacent so don't check those
+ if( i == 0 && ((j + 1) % numControlPointSegments == 0))
+ {
+ j += 1;
+ continue;
+ }
+
+ LineSegment distanceSegment = lineSegments.get(i).closestDistanceToLineSegment(lineSegments.get(j));
+ distances.add(distanceSegment.getLength());
+ j += 1;
+ }
+ }
+
+ return distances.toArray(new Double[distances.size()]);
+ }
+
+ /**
+ * Compares all line segments to all other line segments and returns the minimums of all distances
+ * @param lineSegments
+ * @return {@link Double} the minimum distance of all comparisons
+ */
+ public static Double calculateMinimumDistanceBetweenAllSegments(ArrayList lineSegments)
+ {
+ Double[] distances = calculateDistancesBetweenAllSegments(lineSegments);
+ Arrays.sort(distances); //Sorts the array in increasing order
+ return distances[0];
+ }
+
+ /**
+ * Calculates the distances between all line segments and all other line segments
+ * @param lineSegments
+ * @return {@link Double[]} array of all the distances from comparing the segments.
+ */
+ public static Double[] calculateDistancesBetweenAllSegments(ArrayList lineSegments)
+ {
+ ArrayList distances = new ArrayList();
+ for(int i = 0; i < lineSegments.size(); i++)
+ {
+ LineSegment segment = lineSegments.get(i);
+ for(int j = i ; j < lineSegments.size(); j++)
+ {
+ LineSegment comparingSegment = lineSegments.get(j);
+ distances.add(segment.closestDistanceToLineSegment(comparingSegment).getLength());
+ }
+ }
+ return distances.toArray(new Double[distances.size()]);
+ }
+
+ /**
+ * Removes the shorteningDistance from each line segment and returns the resulting ArrayList
+ * @param segmentsToBeShortened
+ * @param shorteningDistance
+ * @return
+ */
+ public static ArrayList shortenSegments(ArrayList segmentsToBeShortened, double shorteningDistance) throws Exception
+ {
+ ArrayList shortenedSegments = new ArrayList();
+
+ for(LineSegment segment : segmentsToBeShortened)
+ {
+ LineSegment shortenedSegment = shortenLineSegmentEnds(segment, shorteningDistance);
+ shortenedSegments.add(shortenedSegment);
+ }
+
+ return shortenedSegments;
+ }
+
+ /**
+ * Shortens the given segment by the shorteningDistance. It will remove shorteningDistanceFromEachEnd from
+ * each side of the segment so that the total length reduction is 2 *shorteningDistance.
+ * @param segment
+ * @param shorteningDistanceFromEachEnd
+ * @return the new LineSegment of the shortened length
+ */
+ public static LineSegment shortenLineSegmentEnds(LineSegment segment, double shorteningDistanceFromEachEnd) throws Exception
+ {
+ /**
+ * Parametrize the line segment so that you can subtract the distance. Since the line is
+ * parametrized its distance will be from 0 to 1. To reduce it by the desired amount we need
+ * to figure out the proportion of the line compared to the parameter and then subtract that
+ * amount.
+ */
+ double length = segment.getLength();
+ if(2 * shorteningDistanceFromEachEnd > length)
+ throw new Exception("Line Segment isn't long enough to be shortened by that amount. It would result in a length less than 0.");
+
+ PVector lineVector = segment.getStartPoint().sub(segment.getEndPoint());
+ double vectorReduction = shorteningDistanceFromEachEnd / length; //Proportion
+
+ float newEndX = (float) (segment.getEndPoint().x + vectorReduction * lineVector.x);
+ float newEndY = (float) (segment.getEndPoint().y + vectorReduction * lineVector.y);
+ float newEndZ = (float) (segment.getEndPoint().z + vectorReduction * lineVector.z);
+
+ PVector newEndPoint = new PVector(newEndX, newEndY, newEndZ);
+
+ float newStartX = (float) (segment.getEndPoint().x + (1 - vectorReduction) * lineVector.x);
+ float newStartY = (float) (segment.getEndPoint().y + (1 - vectorReduction) * lineVector.y);
+ float newStartZ = (float) (segment.getEndPoint().z + (1 - vectorReduction) * lineVector.z);
+
+ PVector newStartPoint = new PVector(newStartX, newStartY, newStartZ);
+
+ return new LineSegment(newStartPoint, newEndPoint);
+ }
+
+ /**
+ * This generates the omega one value from Professor Peters' and Ji Li's paper. Omega one is used in combination with the delta
+ * from the Denne-Sullivan paper to figure out m1.
+ * @return double - the omega one value
+ */
+ public static double calculateOmegaOne(ArrayList knotPoints, boolean isCurveClosed)
+ {
+ double[] coordinateNorms = {0.0, 0.0, 0.0};
+
+ /**
+ * Generates the set of second iterated forward difference operator. This comes from Professor Peters' and
+ * Ji Li's paper. It is defined in the paper. It shows up as a triangle with a subscript 2 and set P next to it
+ */
+ //TODO: PDZ- might need to adjust this to make the curve either open or closed - it needs to wrap the points (modulo)
+ for(int i = 0; i < knotPoints.size(); i++)
+ {
+ double valueX, valueY, valueZ;
+ if(i == knotPoints.size() - 1)
+ {
+ valueX = knotPoints.get(1).x - (2 * knotPoints.get(0).x) + knotPoints.get(i).x;
+ valueY = knotPoints.get(1).y - (2 * knotPoints.get(0).y) + knotPoints.get(i).y;
+ valueZ = knotPoints.get(1).z - (2 * knotPoints.get(0).z) + knotPoints.get(i).z;
+ }
+ else if((i + 1) == knotPoints.size() - 1)
+ {
+ valueX = knotPoints.get(0).x - (2 * knotPoints.get(i+1).x) + knotPoints.get(i).x;
+ valueY = knotPoints.get(0).y - (2 * knotPoints.get(i+1).y) + knotPoints.get(i).y;
+ valueZ = knotPoints.get(0).z - (2 * knotPoints.get(i+1).z) + knotPoints.get(i).z;
+ }
+ else
+ {
+ valueX = knotPoints.get(i + 2).x - (2 * knotPoints.get(i+1).x) + knotPoints.get(i).x;
+ valueY = knotPoints.get(i + 2).y - (2 * knotPoints.get(i+1).y) + knotPoints.get(i).y;
+ valueZ = knotPoints.get(i + 2).z - (2 * knotPoints.get(i+1).z) + knotPoints.get(i).z;
+ }
+
+ /*
+ * Generate the l1- norm for the given coordinate by adding the absolute values of all the deltaSub2
+ */
+ coordinateNorms[0] += Math.abs(valueX);
+ coordinateNorms[1] += Math.abs(valueY);
+ coordinateNorms[2] += Math.abs(valueZ);
+ }
+
+ /**
+ * Get omega one - which is the largest of all the l1 - norms for x, y, and z
+ */
+ return Math.max(coordinateNorms[0], Math.max(coordinateNorms[1], coordinateNorms[2]));
+ }
+
+ /**
+ * Calculates the M1 value from Professor Peters' and Ji Li's paper
+ * @param omegaOne
+ * @param delta
+ * @return int - the value of M1
+ */
+ public static int calculateM1(double omegaOne, double delta)
+ {
+ double omegaOneSquared = Math.pow(omegaOne, 2);
+ double deltaSquared = Math.pow(delta, 2);
+ double intermediate = ((7.0 / 16.0) * omegaOneSquared * (1 / deltaSquared)) - (1.0 / 7.0);
+ double logResult = Math.log(intermediate) / Math.log(2);
+ return (int) Math.ceil(logResult);
+ }
+
+ /**
+ * Generates the hodograph delta sub 2 set from Professor Peters' and Ji Li's paper. It is defined in
+ * the paper.
+ * @param controlPoints
+ * @return- ArrayList[] - an array of array lists of doubles. Each coordinate (x, y, z) has its set
+ * generated and all 3 sets are returned.
+ */
+ public static ArrayList[] generateHodographDeltaTwoSet(ArrayList controlPoints)
+ {
+ //An array of array lists. There are 3 array lists in the array - one for each component (x, y, and z)
+ @SuppressWarnings("unchecked")
+ ArrayList[] hodographDelta2Sets = (ArrayList[]) new ArrayList[3];
+ hodographDelta2Sets[0] = new ArrayList();
+ hodographDelta2Sets[1] = new ArrayList();
+ hodographDelta2Sets[2] = new ArrayList();
+
+
+ //PDZ- need to wrap around and connect back to the first point
+ for(int i = 1; i < controlPoints.size(); i++) //Add -1 to controlPoints.size() to make it an open curve again
+ {
+ int numControlPoints = controlPoints.size();
+
+ //TODO: fix this - need to move numControlPoints to multiply by everything else
+ double hodographDeltaSub2Ofi_xcoord = numControlPoints * (controlPoints.get((i + 2) % numControlPoints).x
+ - (3 * controlPoints.get((i + 1) % numControlPoints).x)
+ + (3 * controlPoints.get(i).x)
+ - controlPoints.get(i - 1).x);
+ double hodographDeltaSub2Ofi_ycoord = numControlPoints * (controlPoints.get((i + 2) % numControlPoints).y
+ - (3 * controlPoints.get((i + 1) % numControlPoints).y)
+ + (3 * controlPoints.get(i).y)
+ - controlPoints.get(i - 1).y);
+ double hodographDeltaSub2Ofi_zcoord = numControlPoints * (controlPoints.get((i + 2) % numControlPoints).z
+ - (3 * controlPoints.get((i + 1) % numControlPoints).z)
+ + (3 * controlPoints.get(i).z)
+ - controlPoints.get(i - 1).z);
+
+ hodographDelta2Sets[0].add(hodographDeltaSub2Ofi_xcoord);
+ hodographDelta2Sets[1].add(hodographDeltaSub2Ofi_ycoord);
+ hodographDelta2Sets[2].add(hodographDeltaSub2Ofi_zcoord);
+ }
+ return hodographDelta2Sets;
+ }
+
+ /**
+ * Calculates the omega two value from Professor Peters' and Ji Li's paper. Omega two is
+ * calculated by generating the hodograph delta two sets for each coordinate (x, y, and z)
+ * and then taking the max of the l1-norms of those.
+ * @param controlPoints
+ * @return double - the omega two value
+ */
+ public static double calculateOmegaTwo(ArrayList controlPoints)
+ {
+ ArrayList[] hodographSets = generateHodographDeltaTwoSet(controlPoints);
+ double l1NormX = MathUtil.calculateL1Norm(UnboxUtils.unboxDoubles(hodographSets[0]));
+ double l1NormY = MathUtil.calculateL1Norm(UnboxUtils.unboxDoubles(hodographSets[1]));
+ double l1NormZ = MathUtil.calculateL1Norm(UnboxUtils.unboxDoubles(hodographSets[2]));
+ return Math.max(l1NormX, Math.max(l1NormY, l1NormZ));
+ }
+
+ /**
+ * Calculates the lambda of the given segments. The lambda (as defined in Professor Peters' paper) is
+ * the minimum length of all the segments passed in.
+ * @param segments
+ * @return
+ */
+ public static double calculateLambda(ArrayList segments)
+ {
+ double lambda = segments.get(0).getLength();
+ for(LineSegment segment : segments)
+ {
+ lambda = segment.getLength() < lambda ? segment.getLength() : lambda;
+ }
+ return lambda;
+ }
+
+ /**
+ * Calculates m2 from Professor Peters' and Ji Li's paper.
+ * @param segments
+ * @param omegaTwo
+ * @param lambda
+ * @return int - the value of m2
+ */
+ public static int calculateM2(ArrayList segments, double omegaTwo, double lambda)
+ {
+ int m2 = 0;
+ int numControlPoints = segments.size();
+ while((numControlPoints * Math.pow(2, 3 * m2) + Math.pow(2, 2 * m2)) < Math.pow(omegaTwo / lambda, 2))
+ {
+ m2++;
+ }
+ return m2;
+ }
+
+ /**
+ * Calculates m3 from Professor Peters' and Ji Li's paper
+ * @param segments
+ * @param omegaTwo
+ * @param lambda
+ * @return int - the value of m3
+ */
+ public static int calculateM3(ArrayList segments, double omegaTwo , double lambda)
+ {
+ int m3 = 0;
+ int numControlPoints = segments.size();
+ while((numControlPoints * Math.pow(2, 3 * m3) + Math.pow(2, 2 * m3)) < Math.pow((omegaTwo / (Math.sin(Math.PI / 8) * lambda)), 2))
+ {
+ m3++;
+ }
+ return m3;
+ }
+}
diff --git a/src/com/knotrenderer/util/UnboxUtils.java b/src/com/knotrenderer/util/UnboxUtils.java
new file mode 100644
index 0000000..24720dc
--- /dev/null
+++ b/src/com/knotrenderer/util/UnboxUtils.java
@@ -0,0 +1,28 @@
+package com.knotrenderer.util;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+
+public class UnboxUtils
+{
+ /**
+ * Creates an array of doubles (primitive) equal to the values in the array list
+ * @param valuesToUnbox
+ * @return double[] - the unboxed doubles
+ */
+ public static double[] unboxDoubles(ArrayList valuesToUnbox)
+ {
+ return unboxDoubles(valuesToUnbox.toArray(new Double[valuesToUnbox.size()]));
+ }
+
+ /**
+ * Creates an array of doubles (primitive) equal to the values in the array of Doubles
+ * @param valuesToUnbox
+ * @return double[] - the unboxed doubles
+ */
+ public static double[] unboxDoubles(Double[] valuesToUnbox)
+ {
+ return Arrays.stream(valuesToUnbox).mapToDouble(i -> i).toArray();
+ }
+
+}
diff --git a/src/com/knotrenderer/view/ControlP5Controller.java b/src/com/knotrenderer/view/ControlP5Controller.java
new file mode 100644
index 0000000..75a632a
--- /dev/null
+++ b/src/com/knotrenderer/view/ControlP5Controller.java
@@ -0,0 +1,15 @@
+package com.knotrenderer.view;
+
+import controlP5.ControlP5;
+import processing.core.PApplet;
+
+public class ControlP5Controller
+{
+ private final ControlP5 controlP5;
+
+ public ControlP5Controller(PApplet applet)
+ {
+ controlP5 = new ControlP5(applet);
+ new ControlP5CurveLoaderTab(controlP5);
+ }
+}
diff --git a/src/com/knotrenderer/view/ControlP5CurveLoaderTab.java b/src/com/knotrenderer/view/ControlP5CurveLoaderTab.java
new file mode 100644
index 0000000..ed4a83b
--- /dev/null
+++ b/src/com/knotrenderer/view/ControlP5CurveLoaderTab.java
@@ -0,0 +1,33 @@
+package com.knotrenderer.view;
+
+import controlP5.ControlP5;
+
+public class ControlP5CurveLoaderTab
+{
+ private static final int TAB_BACKGROUND = 0xFFFDE7F1;
+ private final ControlP5 controlP5;
+
+ public ControlP5CurveLoaderTab(ControlP5 controlP5Instance)
+ {
+ controlP5 = controlP5Instance;
+ initTab();
+ }
+
+ private void initTab()
+ {
+ controlP5.addTab("extra")
+ .setColorBackground(TAB_BACKGROUND)
+ .setColorLabel(0x135eda)
+ .setColorActive(TAB_BACKGROUND)
+ .setLabel("TEST")
+ .setPosition(100, 200);
+
+ controlP5.addButton("button")
+ .setBroadcast(false)
+ .setPosition(100,100)
+ .setSize(80,40)
+ .setValue(1)
+ .setBroadcast(true)
+ .getCaptionLabel().align(ControlP5.CENTER, ControlP5.CENTER);
+ }
+}
diff --git a/src/com/knotrenderer/view/KnotRenderer.java b/src/com/knotrenderer/view/KnotRenderer.java
new file mode 100644
index 0000000..0dacb01
--- /dev/null
+++ b/src/com/knotrenderer/view/KnotRenderer.java
@@ -0,0 +1,196 @@
+package com.knotrenderer.view;
+
+import com.knotrenderer.model.Curve;
+import com.knotrenderer.util.CurveLoader;
+
+import processing.core.PApplet;
+import processing.event.KeyEvent;
+import processing.event.MouseEvent;
+
+public class KnotRenderer extends PApplet
+{
+// private static final float CAMERA_START_Z = 40f;
+// private static final float ZOOM_MULTIPLIER = 20.f;
+ private static final float CAMERA_START_Z = 40f;
+ private static final float ZOOM_MULTIPLIER = 5.f;
+
+ private static int WIDTH = 640;
+ private static int HEIGHT = 480;
+
+ private RenderCurve renderCurve;
+ private float cameraX = WIDTH / 2;
+ private float cameraY = HEIGHT / 2;
+ private float cameraZ = CAMERA_START_Z;
+
+ private float startMouseX;
+ private float startMouseY;
+ private float rotationX = 0;
+ private float rotationY = 0;
+
+ public static void main(String[] args)
+ {
+ PApplet.main(new String[] {KnotRenderer.class.getName()});
+ }
+
+ public void test()
+ {
+// getSurface().get
+ }
+ @Override
+ public void settings()
+ {
+ size(WIDTH, HEIGHT, "processing.opengl.PGraphics3D");
+ }
+
+ @Override
+ public void setup()
+ {
+ new ControlP5Controller(this);
+
+ surface.setResizable(true);
+
+
+// Curve curve = CurveLoader.loadCurveFromFile("D:/Peter/College_Grad_School/Independent_Study_Spring_2016_Cont/Knot-Renderer-Processing/Testing_Files/TJP-4th-C1-high-precision.curve");
+// Curve curve = CurveLoader.loadCurveFromFile("D:/Peter/College_Grad_School/Independent_Study_Spring_2016_Cont/Knot-Renderer-Processing/Testing_Files/TJP-4_1_stick-unknot-halved-3.curve");
+ Curve curve = CurveLoader.loadCurveFromFile("D:/Peter/College_Grad_School/Independent_Study_Spring_2016_Cont/Knot-Renderer-Processing/Testing_Files/test.curve");
+
+// Curve curve = CurveLoader.loadCurveFromFile("/Users/peterzaffetti/UC/Knot-Renderer-Processing/Testing_Files/TJP-4_1_stick-unknot.curve");
+// Curve curve = CurveLoader.loadCurveFromFile("/Users/peterzaffetti/UC/Knot-Renderer-Processing/Testing_Files/test.curve");
+// Curve curve = CurveLoader.loadCurveFromFile("/Users/peterzaffetti/UC/Knot-Renderer-Processing/Testing_Files/TJP-4_1_stick-unknot-halved-3.curve");
+ renderCurve = new RenderCurve(this, curve);
+ }
+
+ @Override
+ public void mouseWheel(MouseEvent event)
+ {
+ cameraZ -= ZOOM_MULTIPLIER * event.getCount();
+ }
+
+ @Override
+ public void frameResized(int w, int h) {
+ super.frameResized(w, h);
+ WIDTH = w;
+ System.out.println(WIDTH);
+ HEIGHT = h;
+ }
+
+ @Override
+ public void keyPressed(KeyEvent event) {
+ super.keyPressed(event);
+ if(event.getKey() == CODED)
+ {
+ int keyCode = event.getKeyCode();
+ switch (keyCode)
+ {
+ case UP:
+ rotationX += Math.PI / 6;
+ break;
+ case DOWN:
+ rotationX -= Math.PI / 6;
+ break;
+ case LEFT:
+ rotationY -= Math.PI / 6;
+ break;
+ case RIGHT:
+ rotationY += Math.PI / 6;
+ break;
+ }
+ }
+ else
+ {
+ switch(event.getKey())
+ {
+ case ' ':
+ //PDZ- when the user presses space make sure that the WIDTH and HEIGHT are updated to the latest
+ WIDTH = width;
+ HEIGHT = height;
+
+ //TODO: PDZ- Doing it this way will apply the same transformation to every curve. Might want to make it so that you
+ //can control/rotate/translate each curve individually **SEE BELOW**
+ cameraX = WIDTH/2;
+ cameraY = HEIGHT/2;
+
+ //PDZ- Uncomment these if you want the space bar to also undo any rotations and scaling of the knot/curve
+// cameraZ = CAMERA_START_Z;
+// rotationX = 0;
+// rotationY = 0;
+ break;
+ case 'w':
+ cameraY -= 20;
+ break;
+ case 's':
+ cameraY += 20;
+ break;
+ case 'a':
+ cameraX -=20;
+ break;
+ case 'd':
+ cameraX +=20;
+ break;
+ }
+
+ }
+ }
+
+ @Override
+ public void mouseDragged(MouseEvent event)
+ {
+ //TODO: PDZ- Fix the issue where panning will slightly rotate the object (might be an issue with the camera)
+ if(mousePressed && (mouseButton == LEFT))
+ {
+ if(startMouseX < 1)
+ {
+ startMouseX = mouseX;
+ }
+ float deltaX = mouseX - startMouseX;
+ cameraX += deltaX;
+ startMouseX = mouseX;
+
+ if(startMouseY < 1)
+ {
+ startMouseY = mouseY;
+ }
+ float deltaY = mouseY - startMouseY;
+ cameraY += deltaY;
+ startMouseY = mouseY;
+ }
+ else if(mousePressed && (mouseButton == RIGHT))
+ {
+
+ }
+ }
+
+ @Override
+ public void mouseClicked(MouseEvent event)
+ {
+ startMouseX = mouseX;
+ startMouseY = mouseY;
+ }
+
+ @Override
+ public void draw()
+ {
+ background(0);
+
+ //PDZ- camera needs to be first since the translation messes up the coordinate
+ pushMatrix();
+ camera(WIDTH / 2, HEIGHT / 2, 40f, WIDTH/2.0f, HEIGHT/2.0f, 0f, 0f, 1f, 0f);
+ popMatrix();
+
+ pushMatrix();
+ //TODO: PDZ- see comment above about separating the camera from the curves **SEE ABOVE**
+ // You would just need to apply the translation/rotation in the curve draw methods
+ translate(cameraX, cameraY, cameraZ);
+ stroke(255);
+ strokeWeight(0.2f);
+ rotateX(rotationX);
+ rotateY(rotationY);
+ scale(20); //PDZ- this needs to come after the translation
+ noFill();
+
+ renderCurve.drawBezierCurve();
+ renderCurve.drawStickKnot();
+ popMatrix();
+ }
+
+}
diff --git a/src/com/knotrenderer/view/KnotRendererApplet.java b/src/com/knotrenderer/view/KnotRendererApplet.java
new file mode 100644
index 0000000..413bc85
--- /dev/null
+++ b/src/com/knotrenderer/view/KnotRendererApplet.java
@@ -0,0 +1,27 @@
+package com.knotrenderer.view;
+
+import java.applet.Applet;
+import java.awt.Graphics;
+
+public class KnotRendererApplet extends Applet
+{
+ @Override
+ public void init() {
+ super.init();
+
+ KnotRenderer renderer = new KnotRenderer();
+// renderer.
+
+
+ }
+
+ @Override
+ public void start() {
+ super.start();
+ }
+
+ @Override
+ public void paint(Graphics g) {
+ super.paint(g);
+ }
+}
diff --git a/src/com/knotrenderer/view/RenderCurve.java b/src/com/knotrenderer/view/RenderCurve.java
new file mode 100644
index 0000000..10e6f60
--- /dev/null
+++ b/src/com/knotrenderer/view/RenderCurve.java
@@ -0,0 +1,176 @@
+package com.knotrenderer.view;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.concurrent.Semaphore;
+
+import com.knotrenderer.model.BezierCurve;
+import com.knotrenderer.model.Curve;
+
+import processing.core.PApplet;
+import processing.core.PVector;
+
+/**
+ * PDZ- This class is backed by a {@link Curve} as its model. However, this curve, since it is being
+ * drawn and needs to be drawn smoothly, has additional draw points which we calculate here and don't
+ * want to mix in with the model points. The additional points are for the smoothness of the Bezier
+ * curve. This class also holds the draw methods for both the stick knot and Bezier curve of the
+ * model knot.
+ *
+ */
+public class RenderCurve
+{
+ Thread testThread;
+ private PApplet applet;
+ private Curve knotCurve;
+ private BezierCurve bezierCurve;
+ private ArrayList knotPoints;
+ private ArrayList bezierDrawPoints;
+ private ArrayList testPoints;
+ private float scale = 1.f;
+ private static final Semaphore semaphore = new Semaphore(1);
+
+ //PDZ- For testing:
+ private PVector recursive;
+ private PVector summation;
+
+ public RenderCurve(PApplet applet, Curve knotCurve)
+ {
+ this.applet = applet;
+ this.knotCurve = knotCurve;
+ this.knotPoints = knotCurve.getKnotPoints();
+ this.bezierDrawPoints = new ArrayList();
+ this.testPoints = new ArrayList();
+ this.bezierCurve = new BezierCurve(knotPoints);
+ //TODO: PDZ- figure out a better way to handle the mess of recursion when calculating the values
+ testThread = new Thread(new Runnable() {
+
+ @Override
+ public void run() {
+
+ try {
+ semaphore.acquire(1);
+ try
+ {
+ calculateBezierDrawPoints();
+ calculateBezierDrawPoints_recurive();
+ }
+ finally
+ {
+ semaphore.release(1);
+ }
+ } catch (InterruptedException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+ });
+
+ testThread.start();
+// calculateBezierDrawPoints_recurive();
+// calculateBezierDrawPoints();
+// PVector bezierPoint = bezierCurve.getCurveValueFromExpansion(.1);
+// this.bezierDrawPoints.add(bezierPoint);
+ }
+
+ private void calculateBezierDrawPoints_recurive()
+ {
+ for(double i = 0; i <= 1; i+= 0.0001)
+ {
+ PVector bezierPoint = bezierCurve.getCurveValueRecursively(i);
+ if(i == 0.25)
+ {
+ recursive = bezierPoint.copy();
+ }
+ this.bezierDrawPoints.add(bezierPoint);
+ }
+ }
+
+ private void calculateBezierDrawPoints()
+ {
+ for(double t = 0; t <= 1; t += 0.0001) //PDZ- Had to crank 0.001 to 0.0001 since there were gaps for the super high precision curve
+ {
+ PVector bezierPoint = bezierCurve.getCurveValueFromSummationExpansion(t);
+ if(t == 0.25)
+ {
+ summation = bezierPoint.copy();
+ }
+ this.testPoints.add(bezierPoint);
+ }
+ }
+
+ public void setScale(float scale)
+ {
+ this.scale = scale;
+ }
+
+ public void drawStickKnot()
+ {
+ applet.stroke(255, 255, 255);
+ applet.scale(scale);
+ ArrayList points = knotCurve.getKnotPoints();
+
+ applet.beginShape();
+ Iterator it = points.iterator();
+ while(it.hasNext())
+ {
+ PVector vec = it.next();
+ applet.vertex(vec.x, vec.y, vec.z);
+ }
+ applet.endShape(PApplet.CLOSE);
+ }
+
+ public void drawBezierCurve()
+ {
+ boolean didAcquire = semaphore.tryAcquire(1);
+ if(!didAcquire || bezierDrawPoints.size() < 1)
+ {
+// System.out.println("HOPE");
+ return;
+ }
+
+ recursive = bezierCurve.getCurveValueRecursively(0.25);
+ summation = bezierCurve.getCurveValueFromSummationExpansion(0.25);
+
+ System.out.println("Recurisve = " + recursive);
+ System.out.println("Summation = " + summation);
+
+ applet.stroke(255, 0, 0);
+ applet.scale(scale);
+ applet.beginShape();
+ applet.vertex(bezierDrawPoints.get(0).x, bezierDrawPoints.get(0).y, bezierDrawPoints.get(0).z);
+ Iterator it = bezierDrawPoints.iterator();
+ int j = 0;
+ while(it.hasNext())
+ {
+// j++;
+// if(j == 150)
+// break;
+ PVector bezierPoint = it.next();
+ applet.vertex(bezierPoint.x, bezierPoint.y, bezierPoint.z);
+ }
+ applet.endShape();
+
+ if(!didAcquire)
+ {
+ return;
+ }
+
+ applet.stroke(0, 255, 0);
+ applet.scale(scale);
+ applet.beginShape();
+ applet.vertex(testPoints.get(0).x, testPoints.get(0).y, testPoints.get(0).z);
+ Iterator iterator = testPoints.iterator();
+ int i = 0;
+ while(iterator.hasNext())
+ {
+// i++;
+// if(i == 150)
+// break;
+ PVector bezierPoint = iterator.next();
+ applet.vertex(bezierPoint.x, bezierPoint.y, bezierPoint.z);
+ }
+ applet.endShape();
+ semaphore.release(1);
+ }
+}
diff --git a/src/com/knotrenderer/web/PointGeneratorServlet.java b/src/com/knotrenderer/web/PointGeneratorServlet.java
new file mode 100644
index 0000000..4546df6
--- /dev/null
+++ b/src/com/knotrenderer/web/PointGeneratorServlet.java
@@ -0,0 +1,41 @@
+package com.knotrenderer.web;
+
+import java.io.IOException;
+import javax.servlet.ServletException;
+import javax.servlet.annotation.WebServlet;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * Servlet implementation class PointGeneratorServlet
+ */
+@WebServlet("/PointGeneratorServlet")
+public class PointGeneratorServlet extends HttpServlet {
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * @see HttpServlet#HttpServlet()
+ */
+ public PointGeneratorServlet() {
+ super();
+ // TODO Auto-generated constructor stub
+ }
+
+ /**
+ * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
+ */
+ protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
+ // TODO Auto-generated method stub
+ response.getWriter().append("Served at: ").append(request.getContextPath());
+ }
+
+ /**
+ * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
+ */
+ protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
+ // TODO Auto-generated method stub
+ doGet(request, response);
+ }
+
+}