aboutsummaryrefslogtreecommitdiff
path: root/js/lib/geom/sub-path.js
diff options
context:
space:
mode:
authorPushkar Joshi2012-04-10 14:18:02 -0700
committerPushkar Joshi2012-04-10 14:18:02 -0700
commit7fed1940bb4f3a333cef92fd51787a29e6dd787b (patch)
treeb7d5298604e8e1d323c51b2729f1f98daea31e6a /js/lib/geom/sub-path.js
parentdae3041e6b8269da3d593a44c09e2288bb434a02 (diff)
downloadninja-7fed1940bb4f3a333cef92fd51787a29e6dd787b.tar.gz
compute and store local coordinates for all anchor points and their control handles separately, AND more hit testing with local coordinates
Diffstat (limited to 'js/lib/geom/sub-path.js')
-rwxr-xr-xjs/lib/geom/sub-path.js165
1 files changed, 138 insertions, 27 deletions
diff --git a/js/lib/geom/sub-path.js b/js/lib/geom/sub-path.js
index 33bcfc9a..64f27bd3 100755
--- a/js/lib/geom/sub-path.js
+++ b/js/lib/geom/sub-path.js
@@ -57,7 +57,7 @@ var GLSubpath = function GLSubpath() {
57 this._LocalPoints = []; //polyline representation of this curve in canvas space 57 this._LocalPoints = []; //polyline representation of this curve in canvas space
58 this._LocalBBoxMin = [0,0,0]; //bbox min point of _LocalPoints 58 this._LocalBBoxMin = [0,0,0]; //bbox min point of _LocalPoints
59 this._LocalBBoxMax = [0,0,0]; //bbox max point of _LocalPoints 59 this._LocalBBoxMax = [0,0,0]; //bbox max point of _LocalPoints
60 60 this._AnchorLocalCoords = []; //local coords for all the anchor points , stored as a triplet of 3D vectors (prev, curr, next, in order)
61 this._UnprojectedAnchors = []; 61 this._UnprojectedAnchors = [];
62 62
63 //initially set the _dirty bit so we will construct samples 63 //initially set the _dirty bit so we will construct samples
@@ -66,6 +66,9 @@ var GLSubpath = function GLSubpath() {
66 //initially set the local dirty bit so we will construct local coordinates 66 //initially set the local dirty bit so we will construct local coordinates
67 this._isLocalDirty = true; 67 this._isLocalDirty = true;
68 68
69 //initially set the local dirty bit for the anchors so we will construct local coordinates
70 this._isAnchorLocalDirty = true;
71
69 //whether or not to use the canvas drawing to stroke/fill 72 //whether or not to use the canvas drawing to stroke/fill
70 this._useCanvasDrawing = true; 73 this._useCanvasDrawing = true;
71 74
@@ -92,8 +95,8 @@ var GLSubpath = function GLSubpath() {
92 95
93 //tool that owns this subpath 96 //tool that owns this subpath
94 this._drawingTool = null; 97 this._drawingTool = null;
95 this._planeMat = null; 98 this._planeMat = Matrix.I(4);
96 this._planeMatInv = null; 99 this._planeMatInv = Matrix.I(4);
97 this._planeCenter = null; 100 this._planeCenter = null;
98 this._dragPlane = null; 101 this._dragPlane = null;
99 102
@@ -498,6 +501,27 @@ GLSubpath.prototype.insertAnchorAtParameter = function(index, param) {
498 this._dirty = true; 501 this._dirty = true;
499}; 502};
500 503
504GLSubpath.prototype._checkIntersectionWithSampleLocalCoord = function(startIndex, endIndex, point, radius){
505 //check whether the point is within the radius distance from the curve represented as a polyline in _samples
506 //return the parametric distance along the curve if there is an intersection, else return null
507 //will assume that the BBox test is performed outside this function
508 if (endIndex<startIndex){
509 //go from startIndex to the end of the samples
510 endIndex = this._LocalPoints.length;
511 }
512 for (var i=startIndex; i<endIndex-1; i++){
513 var seg0 = this._LocalPoints[i].slice(0);
514 var j=i+1;
515 var seg1 = this._LocalPoints[j].slice(0);
516 var distToSegment = MathUtils.distPointToSegment(point, seg0, seg1);
517 if (distToSegment<=radius){
518 var paramDistance = MathUtils.paramPointProjectionOnSegment(point, seg0, seg1); //TODO Optimize! this function was called in distPointToSegment above
519 return this._sampleParam[i] + (this._sampleParam[j] - this._sampleParam[i])*paramDistance;
520 }
521 }
522 return null;
523};
524
501GLSubpath.prototype._checkIntersectionWithSamples = function(startIndex, endIndex, point, radius){ 525GLSubpath.prototype._checkIntersectionWithSamples = function(startIndex, endIndex, point, radius){
502 //check whether the point is within the radius distance from the curve represented as a polyline in _samples 526 //check whether the point is within the radius distance from the curve represented as a polyline in _samples
503 //return the parametric distance along the curve if there is an intersection, else return null 527 //return the parametric distance along the curve if there is an intersection, else return null
@@ -622,23 +646,24 @@ GLSubpath.prototype._isWithinBoundingBox = function(point, ctrlPts, radius) {
622 646
623GLSubpath.prototype._checkAnchorIntersection = function(pickX, pickY, pickZ, radSq, anchorIndex, minDistance, useLocal) { 647GLSubpath.prototype._checkAnchorIntersection = function(pickX, pickY, pickZ, radSq, anchorIndex, minDistance, useLocal) {
624 //if we are asked to use the local coordinate and the local coordinate for this anchor exists 648 //if we are asked to use the local coordinate and the local coordinate for this anchor exists
625 if (useLocal && this._anchorSampleIndex.length>anchorIndex && this._LocalPoints.length > this._anchorSampleIndex[anchorIndex]) { 649 if (useLocal && this._AnchorLocalCoords.length > anchorIndex) {//this._anchorSampleIndex.length>anchorIndex && this._LocalPoints.length > this._anchorSampleIndex[anchorIndex]) {
626 var localCoord = this._LocalPoints[this._anchorSampleIndex[anchorIndex]] 650 //var localCoord = this._LocalPoints[this._anchorSampleIndex[anchorIndex]];
627 var distSq = VecUtils.vecDistSq(3, [pickX, pickY, pickZ], localCoord); 651 var anchorLocalCoord = this._AnchorLocalCoords[anchorIndex];
652 var distSq = VecUtils.vecDistSq(3, [pickX, pickY, pickZ], anchorLocalCoord[1]);
628 //check the anchor point 653 //check the anchor point
629 if (distSq < radSq && distSq<minDistance) { 654 if (distSq < radSq && distSq<minDistance) {
630 return this.SEL_ANCHOR; 655 return this.SEL_ANCHOR;
631 } 656 }
632 /* 657
633 //check the prev. and next of the selected anchor point 658 //check the prev. and next of the selected anchor point
634 distSq = this._Anchors[anchorIndex].getPrevDistanceSq(pickX, pickY, pickZ); 659 distSq = VecUtils.vecDistSq(3, [pickX, pickY, pickZ], anchorLocalCoord[0]);
635 if (distSq<radSq && distSq<minDistance){ 660 if (distSq<radSq && distSq<minDistance){
636 return this.SEL_PREV; 661 return this.SEL_PREV;
637 } 662 }
638 distSq = this._Anchors[anchorIndex].getNextDistanceSq(pickX, pickY, pickZ); 663 distSq = VecUtils.vecDistSq(3, [pickX, pickY, pickZ], anchorLocalCoord[2]);
639 if (distSq<radSq && distSq<minDistance){ 664 if (distSq<radSq && distSq<minDistance){
640 return this.SEL_NEXT; 665 return this.SEL_NEXT;
641 }*/ 666 }
642 return this.SEL_NONE; 667 return this.SEL_NONE;
643 } 668 }
644 669
@@ -694,6 +719,42 @@ GLSubpath.prototype.isWithinBBox = function(x,y,z) {
694 return true; 719 return true;
695} 720}
696 721
722//pick the path location closest to the input pick point (in local coord)
723GLSubpath.prototype.pathSamplesLocalHitTest = function(pickX, pickY, pickZ, radius){
724 var numAnchors = this._AnchorLocalCoords.length;
725 var selAnchorIndex = -1;
726 var retParam = null;
727 var radSq = radius * radius;
728 var minDistance = Infinity;
729
730 //first check if the input location is within the bounding box
731 var numSegments = this._isClosed ? numAnchors : numAnchors-1;
732 var currAnchor = this._AnchorLocalCoords[0];
733 var nextAnchor=null;
734 for (var i = 0; i < numSegments; i++) {
735 var nextIndex = (i+1)%numAnchors;
736 nextAnchor = this._AnchorLocalCoords[nextIndex];
737 //check if the point is close to the bezier segment between anchor i and anchor nextIndex
738 var controlPoints = [currAnchor[1].slice(0),
739 currAnchor[2].slice(0),
740 nextAnchor[0].slice(0),
741 nextAnchor[1].slice(0)];
742 var point = [pickX, pickY, pickZ];
743 if (this._isWithinBoundingBox(point, controlPoints, radius)) {
744 //var intersectParam = this._checkIntersection(controlPoints, 0.0, 1.0, point, radius);
745 var intersectParam = this._checkIntersectionWithSampleLocalCoord(this._anchorSampleIndex[i], this._anchorSampleIndex[nextIndex], point, radius);
746 if (intersectParam){
747 selAnchorIndex=i;
748 retParam = intersectParam-i; //make the retParam go from 0 to 1
749 break;
750 }
751 }
752 currAnchor = nextAnchor;
753 }//for every anchor i
754 return [selAnchorIndex,retParam];
755
756};
757
697//pick the path point closest to the specified location, return null if some anchor point (or its handles) is within radius, else return the parameter distance 758//pick the path point closest to the specified location, return null if some anchor point (or its handles) is within radius, else return the parameter distance
698GLSubpath.prototype.pathHitTest = function (pickX, pickY, pickZ, radius) { 759GLSubpath.prototype.pathHitTest = function (pickX, pickY, pickZ, radius) {
699 var numAnchors = this._Anchors.length; 760 var numAnchors = this._Anchors.length;
@@ -755,7 +816,6 @@ GLSubpath.prototype.pathHitTest = function (pickX, pickY, pickZ, radius) {
755 if (this._isWithinBoundingBox(point, controlPoints, radius)) { 816 if (this._isWithinBoundingBox(point, controlPoints, radius)) {
756 //var intersectParam = this._checkIntersection(controlPoints, 0.0, 1.0, point, radius); 817 //var intersectParam = this._checkIntersection(controlPoints, 0.0, 1.0, point, radius);
757 var intersectParam = this._checkIntersectionWithSamples(this._anchorSampleIndex[i], this._anchorSampleIndex[nextIndex], point, radius); 818 var intersectParam = this._checkIntersectionWithSamples(this._anchorSampleIndex[i], this._anchorSampleIndex[nextIndex], point, radius);
758 console.log("intersectParam:"+intersectParam);
759 if (intersectParam){ 819 if (intersectParam){
760 selAnchorIndex=i; 820 selAnchorIndex=i;
761 retParam = intersectParam-i; //make the retParam go from 0 to 1 821 retParam = intersectParam-i; //make the retParam go from 0 to 1
@@ -1145,13 +1205,24 @@ GLSubpath.prototype._unprojectPt = function(pt, pespectiveDist) {
1145 1205
1146//return the local coord. of the anchor at the specified index, null if the anchor does not have a local coord yet 1206//return the local coord. of the anchor at the specified index, null if the anchor does not have a local coord yet
1147GLSubpath.prototype.getAnchorLocalCoord = function(index){ 1207GLSubpath.prototype.getAnchorLocalCoord = function(index){
1148 if (this._isDirty){ 1208 if (this._dirty){
1149 this.createSamples(); 1209 this.createSamples();
1150 } 1210 }
1151 if (this._isLocalDirty){ 1211 if (this._isLocalDirty){
1152 this.buildLocalCoord(); 1212 this.buildLocalCoord();
1153 } 1213 }
1154 if (index<0 || index>= this._Anchors.length || index>=this._anchorSampleIndex.length){ 1214 if (index<0 || index>= this._Anchors.length){
1215 return null;
1216 }
1217
1218 if (index>= this._AnchorLocalCoords.length || this._isAnchorLocalDirty){
1219 this._isAnchorLocalDirty = true;
1220 this.buildAnchorLocalCoord();
1221 }
1222
1223 return this._AnchorLocalCoords[index];
1224 /*
1225 if (index>=this._anchorSampleIndex.length){
1155 return null; 1226 return null;
1156 } 1227 }
1157 var anchorSampleIndex = this._anchorSampleIndex[index]; 1228 var anchorSampleIndex = this._anchorSampleIndex[index];
@@ -1160,6 +1231,55 @@ GLSubpath.prototype.getAnchorLocalCoord = function(index){
1160 } 1231 }
1161 var localCoord = this._LocalPoints[anchorSampleIndex].slice(0); 1232 var localCoord = this._LocalPoints[anchorSampleIndex].slice(0);
1162 return localCoord; 1233 return localCoord;
1234 */
1235};
1236
1237GLSubpath.prototype.buildAnchorLocalCoord = function()
1238{
1239 if (!this._isAnchorLocalDirty){
1240 return; //nothing to do
1241 }
1242 var ViewUtils = require("js/helper-classes/3D/view-utils").ViewUtils;
1243 var SnapManager = require("js/helper-classes/3D/snap-manager").SnapManager;
1244
1245 var widthAdjustment = -SnapManager.getStageWidth()*0.5;
1246 var heightAdjustment = -SnapManager.getStageHeight()*0.5;
1247
1248 var drawingCanvas = this.getCanvas();
1249 if (!drawingCanvas){
1250 drawingCanvas = ViewUtils.getStageElement();
1251 }
1252 var localToStageWorldMat = ViewUtils.getLocalToStageWorldMatrix(drawingCanvas, true, false);
1253 var stageWorldToLocalMat = glmat4.inverse(localToStageWorldMat, []);
1254 var numAnchors = this._Anchors.length;
1255 this._AnchorLocalCoords = [];
1256 var i=0, currAnchor, prevLocalCoord, currLocalCoord, nextLocalCoord;
1257 for (i=0;i<numAnchors;i++){
1258 //get the local coordinate of this anchor point
1259 currAnchor = this._Anchors[i];
1