diff options
author | Pushkar Joshi | 2012-04-11 10:07:42 -0700 |
---|---|---|
committer | Pushkar Joshi | 2012-04-11 10:07:42 -0700 |
commit | 25e406c9924438697564bc2341bd5b045a1dd85c (patch) | |
tree | f4c904662708a6d5db8c463a23256ac661c972a1 | |
parent | 7fed1940bb4f3a333cef92fd51787a29e6dd787b (diff) | |
download | ninja-25e406c9924438697564bc2341bd5b045a1dd85c.tar.gz |
Use local coordinates to pick a point within the path (works correctly even with canvas and/or stage transformation). Dragging does not yet work in case of canvas transformation
-rwxr-xr-x | js/lib/geom/sub-path.js | 87 | ||||
-rwxr-xr-x | js/tools/PenTool.js | 20 |
2 files changed, 75 insertions, 32 deletions
diff --git a/js/lib/geom/sub-path.js b/js/lib/geom/sub-path.js index 64f27bd3..87840bdc 100755 --- a/js/lib/geom/sub-path.js +++ b/js/lib/geom/sub-path.js | |||
@@ -752,9 +752,9 @@ GLSubpath.prototype.pathSamplesLocalHitTest = function(pickX, pickY, pickZ, radi | |||
752 | currAnchor = nextAnchor; | 752 | currAnchor = nextAnchor; |
753 | }//for every anchor i | 753 | }//for every anchor i |
754 | return [selAnchorIndex,retParam]; | 754 | return [selAnchorIndex,retParam]; |
755 | |||
756 | }; | 755 | }; |
757 | 756 | ||
757 | |||
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 | 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 |
759 | GLSubpath.prototype.pathHitTest = function (pickX, pickY, pickZ, radius) { | 759 | GLSubpath.prototype.pathHitTest = function (pickX, pickY, pickZ, radius) { |
760 | var numAnchors = this._Anchors.length; | 760 | var numAnchors = this._Anchors.length; |
@@ -828,16 +828,27 @@ GLSubpath.prototype.pathHitTest = function (pickX, pickY, pickZ, radius) { | |||
828 | return [selAnchorIndex,retParam]; | 828 | return [selAnchorIndex,retParam]; |
829 | } //GLSubpath.pathHitTest function | 829 | } //GLSubpath.pathHitTest function |
830 | 830 | ||
831 | |||
831 | //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 | 832 | //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 |
832 | GLSubpath.prototype.pickPath = function (pickX, pickY, pickZ, radius) { | 833 | GLSubpath.prototype.pickPath = function (pickX, pickY, pickZ, radius, useLocal) { |
833 | var numAnchors = this._Anchors.length; | 834 | var numAnchors = this._Anchors.length; |
834 | var selAnchorIndex = -1; | 835 | var selAnchorIndex = -1; |
835 | var retCode = this.SEL_NONE; | 836 | var retCode = this.SEL_NONE; |
836 | var radSq = radius * radius; | 837 | var radSq = radius * radius; |
837 | var minDistance = Infinity; | 838 | var minDistance = Infinity; |
839 | |||
840 | //ignore the useLocal flag if the anchor points are not in-sync. with the local coordinates | ||
841 | if (numAnchors != this._AnchorLocalCoords.length){ | ||
842 | useLocal = false; | ||
843 | } | ||
844 | |||
838 | //check if the clicked location is close to the currently selected anchor position | 845 | //check if the clicked location is close to the currently selected anchor position |
839 | if (this._selectedAnchorIndex>=0 && this._selectedAnchorIndex<this._Anchors.length){ | 846 | if (this._selectedAnchorIndex>=0 && this._selectedAnchorIndex<this._Anchors.length){ |
840 | var distSq = this._Anchors[this._selectedAnchorIndex].getDistanceSq(pickX, pickY, pickZ); | 847 | var distSq; |
848 | if (!useLocal) | ||
849 | distSq = this._Anchors[this._selectedAnchorIndex].getDistanceSq(pickX, pickY, pickZ); | ||
850 | else | ||
851 | distSq = VecUtils.vecDistSq(3, this._AnchorLocalCoords[this._selectedAnchorIndex][1], [pickX, pickY, pickZ]); | ||
841 | //check the anchor point | 852 | //check the anchor point |
842 | if (distSq < minDistance && distSq < radSq) { | 853 | if (distSq < minDistance && distSq < radSq) { |
843 | selAnchorIndex = this._selectedAnchorIndex; | 854 | selAnchorIndex = this._selectedAnchorIndex; |
@@ -845,10 +856,16 @@ GLSubpath.prototype.pickPath = function (pickX, pickY, pickZ, radius) { | |||
845 | retCode = retCode | this.SEL_ANCHOR; | 856 | retCode = retCode | this.SEL_ANCHOR; |
846 | } | 857 | } |
847 | } | 858 | } |
859 | |||
848 | //now check if the click location is close to any anchor position | 860 | //now check if the click location is close to any anchor position |
849 | if (selAnchorIndex===-1) { | 861 | if (selAnchorIndex===-1) { |
850 | for (var i = 0; i < numAnchors; i++) { | 862 | for (var i = 0; i < numAnchors; i++) { |
851 | var distSq = this._Anchors[i].getDistanceSq(pickX, pickY, pickZ); | 863 | var distSq; |
864 | if (!useLocal) | ||
865 | distSq = this._Anchors[i].getDistanceSq(pickX, pickY, pickZ); | ||
866 | else | ||
867 | distSq = VecUtils.vecDistSq(3, this._AnchorLocalCoords[i][1], [pickX, pickY, pickZ]); | ||
868 | |||
852 | //check the anchor point | 869 | //check the anchor point |
853 | if (distSq < minDistance && distSq < radSq) { | 870 | if (distSq < minDistance && distSq < radSq) { |
854 | selAnchorIndex = i; | 871 | selAnchorIndex = i; |
@@ -860,14 +877,23 @@ GLSubpath.prototype.pickPath = function (pickX, pickY, pickZ, radius) { | |||
860 | 877 | ||
861 | //check the prev and next of the selected anchor if the above did not register a hit | 878 | //check the prev and next of the selected anchor if the above did not register a hit |
862 | if (this._selectedAnchorIndex>=0 && selAnchorIndex === -1) { | 879 | if (this._selectedAnchorIndex>=0 && selAnchorIndex === -1) { |
863 | var distSq = this._Anchors[this._selectedAnchorIndex].getPrevDistanceSq(pickX, pickY, pickZ); | 880 | var distSq; |
881 | if (!useLocal) | ||
882 | distSq = this._Anchors[this._selectedAnchorIndex].getPrevDistanceSq(pickX, pickY, pickZ); | ||
883 | else | ||
884 | distSq = VecUtils.vecDistSq(3, this._AnchorLocalCoords[this._selectedAnchorIndex][0], [pickX, pickY, pickZ]); | ||
885 | |||
864 | if (distSq < minDistance && distSq < radSq){ | 886 | if (distSq < minDistance && distSq < radSq){ |
865 | selAnchorIndex = this._selectedAnchorIndex; | 887 | selAnchorIndex = this._selectedAnchorIndex; |
866 | minDistance = distSq; | 888 | minDistance = distSq; |
867 | retCode = retCode | this.SEL_PREV; | 889 | retCode = retCode | this.SEL_PREV; |
868 | } else { | 890 | } else { |
869 | //check the next for this anchor point | 891 | //check the next for this anchor point |
870 | distSq = this._Anchors[this._selectedAnchorIndex].getNextDistanceSq(pickX, pickY, pickZ); | 892 | if (!useLocal) |
893 | distSq = this._Anchors[this._selectedAnchorIndex].getNextDistanceSq(pickX, pickY, pickZ); | ||
894 | else | ||
895 | distSq = VecUtils.vecDistSq(3, this._AnchorLocalCoords[this._selectedAnchorIndex][2], [pickX, pickY, pickZ]); | ||
896 | |||
871 | if (distSq<minDistance && distSq<radSq){ | 897 | if (distSq<minDistance && distSq<radSq){ |
872 | selAnchorIndex = this._selectedAnchorIndex; | 898 | selAnchorIndex = this._selectedAnchorIndex; |
873 | minDistance = distSq; | 899 | minDistance = distSq; |
@@ -881,27 +907,38 @@ GLSubpath.prototype.pickPath = function (pickX, pickY, pickZ, radius) { | |||
881 | this._selectedAnchorIndex = selAnchorIndex; | 907 | this._selectedAnchorIndex = selAnchorIndex; |
882 | } else { | 908 | } else { |
883 | this._selectedAnchorIndex = -1; | 909 | this._selectedAnchorIndex = -1; |
884 | var numSegments = this._isClosed ? numAnchors : numAnchors-1; | 910 | if (!useLocal){ |
885 | for (var i = 0; i < numSegments; i++) { | 911 | var numSegments = this._isClosed ? numAnchors : numAnchors-1; |
886 | var nextIndex = (i+1)%numAnchors; | 912 | var currAnchor = this._AnchorLocalCoords[0]; |
887 | //check if the point is close to the bezier segment between anchor i and anchor nextIndex | 913 | var nextAnchor = null; |
888 | var controlPoints = [[this._Anchors[i].getPosX(),this._Anchors[i].getPosY(),this._Anchors[i].getPosZ()], | 914 | for (var i = 0; i < numSegments; i++) { |
889 | [this._Anchors[i].getNextX(),this._Anchors[i].getNextY(),this._Anchors[i].getNextZ()], | 915 | var nextIndex = (i+1)%numAnchors; |
890 | [this._Anchors[nextIndex].getPrevX(),this._Anchors[nextIndex].getPrevY(),this._Anchors[nextIndex].getPrevZ()], | 916 | //check if the point is close to the bezier segment between anchor i and anchor nextIndex |
891 | [this._Anchors[nextIndex].getPosX(),this._Anchors[nextIndex].getPosY(),this._Anchors[nextIndex].getPosZ()]]; | 917 | var controlPoints = [[this._Anchors[i].getPosX(),this._Anchors[i].getPosY(),this._Anchors[i].getPosZ()], |
892 | var point = [pickX, pickY, pickZ]; | 918 | [this._Anchors[i].getNextX(),this._Anchors[i].getNextY(),this._Anchors[i].getNextZ()], |
893 | if (this._isWithinBoundingBox(point, controlPoints, radius)) { | 919 | [this._Anchors[nextIndex].getPrevX(),this._Anchors[nextIndex].getPrevY(),this._Anchors[nextIndex].getPrevZ()], |
894 | //var intersectParam = this._checkIntersection(controlPoints, 0.0, 1.0, point, radius); | 920 | [this._Anchors[nextIndex].getPosX(),this._Anchors[nextIndex].getPosY(),this._Anchors[nextIndex].getPosZ()]]; |
895 | var intersectParam = this._checkIntersectionWithSamples(this._anchorSampleIndex[i], this._anchorSampleIndex[nextIndex], point, radius); | 921 | var point = [pickX, pickY, pickZ]; |
896 | console.log("intersectParam:"+intersectParam); | 922 | if (this._isWithinBoundingBox(point, controlPoints, radius)) { |
897 | if (intersectParam){ | 923 | //var intersectParam = this._checkIntersection(controlPoints, 0.0, 1.0, point, radius); |
898 | retCode = retCode | this.SEL_PATH; | 924 | var intersectParam = this._checkIntersectionWithSamples(this._anchorSampleIndex[i], this._anchorSampleIndex[nextIndex], point, radius); |
899 | retParam = intersectParam-i; //make the retParam go from 0 to 1 | 925 | console.log("intersectParam:"+intersectParam); |
900 | this._selectedAnchorIndex = i; | 926 | if (intersectParam){ |
901 | break; | 927 | retCode = retCode | this.SEL_PATH; |
928 | retParam = intersectParam-i; //make the retParam go from 0 to 1 | ||
929 | this._selectedAnchorIndex = i; | ||
930 | break; | ||
931 | } | ||
902 | } | 932 | } |
933 | }//for every anchor i | ||
934 | } else { | ||
935 | var selAnchorParam = this.pathSamplesLocalHitTest(pickX, pickY, pickZ, radius); | ||
936 | if (selAnchorParam[0]!== -1){ | ||
937 | this._selectedAnchorIndex = selAnchorParam[0]; | ||
938 | retParam = selAnchorParam[1]; | ||
939 | retCode = retCode | this.SEL_PATH; | ||
903 | } | 940 | } |
904 | }//for every anchor i | 941 | } |
905 | } | 942 | } |
906 | this._selectMode = retCode; | 943 | this._selectMode = retCode; |
907 | return retParam; | 944 | return retParam; |
diff --git a/js/tools/PenTool.js b/js/tools/PenTool.js index cd15b36b..8065e1a6 100755 --- a/js/tools/PenTool.js +++ b/js/tools/PenTool.js | |||
@@ -182,12 +182,23 @@ exports.PenTool = Montage.create(ShapeTool, { | |||
182 | console.log("Warning...PenTool handleMouseDown: changing from SELECT_PATH to SELECT_NONE"); | 182 | console.log("Warning...PenTool handleMouseDown: changing from SELECT_PATH to SELECT_NONE"); |
183 | } | 183 | } |
184 | } | 184 | } |
185 | 185 | ||
186 | //build the mouse down position in local coordinates | ||
187 | var drawingCanvas = this._selectedSubpath.getCanvas(); | ||
188 | if (!drawingCanvas){ | ||
189 | drawingCanvas = ViewUtils.getStageElement(); | ||
190 | } | ||
191 | var globalMousePos = this._getUnsnappedScreenPosition(event.pageX, event.pageY); | ||
192 | var localMousePos = ViewUtils.globalToLocal(globalMousePos, drawingCanvas); | ||
193 | |||
194 | |||
186 | var prevSelectedAnchorIndex = this._selectedSubpath.getSelectedAnchorIndex(); | 195 | var prevSelectedAnchorIndex = this._selectedSubpath.getSelectedAnchorIndex(); |
187 | // ************* Add/Select Anchor Point ************* | 196 | // ************* Add/Select Anchor Point ************* |
188 | //check if the clicked location is close to an anchor point...if so, make that anchor the selected point and do nothing else | 197 | //check if the clicked location is close to an anchor point...if so, make that anchor the selected point and do nothing else |
189 | // BUT if the anchor point selected is the first anchor point, check if the previous selected anchor was the last anchor point...in that case, close the path | 198 | // BUT if the anchor point selected is the first anchor point, check if the previous selected anchor was the last anchor point...in that case, close the path |
190 | var selParam = this._selectedSubpath.pickPath(mouseDownPos[0], mouseDownPos[1], mouseDownPos[2], this._PICK_POINT_RADIUS); | 199 | |
200 | //var selParam = this._selectedSubpath.pickPath(mouseDownPos[0], mouseDownPos[1], mouseDownPos[2], this._PICK_POINT_RADIUS); | ||
201 | var selParam = this._selectedSubpath.pickPath(localMousePos[0], localMousePos[1], localMousePos[2], this._PICK_POINT_RADIUS, true); | ||
191 | var whichPoint = this._selectedSubpath.getSelectedMode(); | 202 | var whichPoint = this._selectedSubpath.getSelectedMode(); |
192 | if (whichPoint & this._selectedSubpath.SEL_ANCHOR) { | 203 | if (whichPoint & this._selectedSubpath.SEL_ANCHOR) { |
193 | //if we're in ENTRY_SELECT_PATH mode AND we have not yet clicked on the endpoint AND if we have now clicked on the endpoint | 204 | //if we're in ENTRY_SELECT_PATH mode AND we have not yet clicked on the endpoint AND if we have now clicked on the endpoint |
@@ -288,9 +299,6 @@ exports.PenTool = Montage.create(ShapeTool, { | |||
288 | }, //HandleLeftButtonDown | 299 | }, //HandleLeftButtonDown |
289 | 300 | ||
290 | 301 | ||
291 | //need to override this function because the ShapeTool's definition contains a clearDrawingCanvas call - Pushkar | ||
292 | // might not need to override once we draw using OpenGL instead of SVG | ||
293 | // Also took out all the snapping code for now...need to add that back | ||
294 | HandleMouseMove: | 302 | HandleMouseMove: |
295 | { | 303 | { |
296 | value: function (event) { | 304 | value: function (event) { |
@@ -647,7 +655,6 @@ exports.PenTool = Montage.create(ShapeTool, { | |||
647 | } //if this is a new path being rendered | 655 | } //if this is a new path being rendered |
648 | 656 | ||
649 | this._selectedSubpath.makeDirty(); | 657 | this._selectedSubpath.makeDirty(); |
650 | |||
651 | this._selectedSubpath.createSamples(); | 658 | this._selectedSubpath.createSamples(); |
652 | //if we have some samples to render... | 659 | //if we have some samples to render... |
653 | if (this._selectedSubpath.getNumAnchors() > 1) { | 660 | if (this._selectedSubpath.getNumAnchors() > 1) { |
@@ -1011,7 +1018,6 @@ exports.PenTool = Montage.create(ShapeTool, { | |||
1011 | ctx.stroke(); | 1018 | ctx.stroke(); |
1012 | } | 1019 | } |
1013 | 1020 | ||
1014 | |||
1015 | //display selected anchor and its prev. and next points | 1021 | //display selected anchor and its prev. and next points |
1016 |