diff options
Diffstat (limited to 'js/tools/PenTool.js')
-rw-r--r-- | js/tools/PenTool.js | 1245 |
1 files changed, 1245 insertions, 0 deletions
diff --git a/js/tools/PenTool.js b/js/tools/PenTool.js new file mode 100644 index 00000000..78344d18 --- /dev/null +++ b/js/tools/PenTool.js | |||
@@ -0,0 +1,1245 @@ | |||
1 | /* <copyright> | ||
2 | This file contains proprietary software owned by Motorola Mobility, Inc.<br/> | ||
3 | No rights, expressed or implied, whatsoever to this software are provided by Motorola Mobility, Inc. hereunder.<br/> | ||
4 | (c) Copyright 2011 Motorola Mobility, Inc. All Rights Reserved. | ||
5 | </copyright> */ | ||
6 | |||
7 | var ShapeTool = require("js/tools/ShapeTool").ShapeTool; | ||
8 | var DrawingToolBase = require("js/tools/drawing-tool-base").DrawingToolBase; | ||
9 | var defaultEventManager = require("montage/core/event/event-manager").defaultEventManager; | ||
10 | var Montage = require("montage/core/core").Montage; | ||
11 | var NJUtils = require("js/lib/NJUtils").NJUtils; | ||
12 | var ElementMediator = require("js/mediators/element-mediator").ElementMediator; | ||
13 | var TagTool = require("js/tools/TagTool").TagTool; | ||
14 | var ElementController = require("js/controllers/elements/element-controller").ElementController; | ||
15 | |||
16 | exports.PenTool = Montage.create(ShapeTool, { | ||
17 | |||
18 | _toolID: { value: "penTool" }, | ||
19 | _imageID: { value: "penToolImg" }, | ||
20 | _toolImageClass: { value: "penToolUp" }, | ||
21 | _selectedToolImageClass: { value: "penToolDown" }, | ||
22 | _toolTipText: { value: "Pen Tool" }, | ||
23 | _penView: { value: null, writable: true }, | ||
24 | |||
25 | _selectedToolClass: { value: "penToolSpecificProperties" }, | ||
26 | _penToolProperties: { enumerable: false, value: null, writable: true }, | ||
27 | _parentNode: { enumerable: false, value: null, writable: true }, | ||
28 | _toolsPropertiesContainer: { enumerable: false, value: null, writable: true }, | ||
29 | |||
30 | // Need to keep track of current mouse position for KEY modifiers event which do not have mouse coordinates | ||
31 | _currentX: { value: 0, writable: true }, | ||
32 | _currentY: { value: 0, writable: true }, | ||
33 | |||
34 | //the subpaths are what is displayed on the screen currently, with _selectedSubpath being the active one currently being edited | ||
35 | _subpaths: { value: [], writable: true }, | ||
36 | _selectedSubpath: { value: null, writable: true }, | ||
37 | _makeMultipleSubpaths: { value: true, writable: true }, //set this to true if you want to keep making subpaths after closing current subpath | ||
38 | |||
39 | |||
40 | //whether or not to display the guides for debugging | ||
41 | _showGuides: { value: true, writable: true }, | ||
42 | |||
43 | //whether the user has held down the Alt key | ||
44 | _isAltDown: { value: false, writable: true }, | ||
45 | |||
46 | //whether the user has held down the Esc key | ||
47 | _isEscapeDown: {value: false, writable: true }, | ||
48 | |||
49 | //whether we have just started a new path (set true in mousedown, and set false in mouse up | ||
50 | _isNewPath: {value: false, writable: true}, | ||
51 | |||
52 | //whether we have clicked one of the endpoints after entering the pen tool in ENTRY_SELECT_PATH edit mode | ||
53 | _isPickedEndPointInSelectPathMode: {value: false, writable: true}, | ||
54 | |||
55 | //when the user wants to place a selected anchor point on top of another point, this is the target where the point will be placed | ||
56 | _snapTarget: { value: null, writable: true }, | ||
57 | |||
58 | //whether or not we're using webgl for drawing | ||
59 | _useWebGL: {value: false, writable: false }, | ||
60 | |||
61 | //the canvas created by the pen tool...this is grown or shrunk with the path (if the canvas was not already provided) | ||
62 | _penCanvas: { value: null, writable: true }, | ||
63 | |||
64 | //the plane matrix for the first click...so the entire path is on the same plane | ||
65 | _penPlaneMat: { value: null, writable: true }, | ||
66 | |||
67 | //constants used for picking points --- NOTE: these should be user-settable parameters | ||
68 | _PICK_POINT_RADIUS: { value: 10, writable: false }, | ||
69 | _DISPLAY_ANCHOR_RADIUS: { value: 5, writable: false }, | ||
70 | _DISPLAY_SELECTED_ANCHOR_RADIUS: { value: 10, writable: false }, | ||
71 | _DISPLAY_SELECTED_ANCHOR_PREV_RADIUS: { value: 5, writable: false }, | ||
72 | _DISPLAY_SELECTED_ANCHOR_NEXT_RADIUS: { value: 5, writable: false }, | ||
73 | |||
74 | //constants used for editing modes (can be OR-ed) | ||
75 | EDIT_NONE: { value: 0, writable: false }, | ||
76 | EDIT_ANCHOR: { value: 1, writable: false }, | ||
77 | EDIT_PREV: { value: 2, writable: false }, | ||
78 | EDIT_NEXT: { value: 4, writable: false }, | ||
79 | EDIT_PREV_NEXT: { value: 8, writable: false }, | ||
80 | _editMode: { value: this.EDIT_NONE, writable: true }, | ||
81 | |||
82 | //constants used for selection modes on entry to pen tool (mutually exclusive) | ||
83 | ENTRY_SELECT_NONE: { value: 0, writable: false}, | ||
84 | ENTRY_SELECT_CANVAS: { value: 1, writable: false}, | ||
85 | ENTRY_SELECT_PATH: { value: 2, writable: false}, | ||
86 | _entryEditMode: {value: this.ENTRY_SELECT_NONE, writable: true}, | ||
87 | |||
88 | // ******** Logic for selection ******* | ||
89 | // (update if you change functionality!) | ||
90 | // NOTE: this is out of date...needs to be updated | ||
91 | // | ||
92 | // Start by setting edit mode to EDIT_NONE | ||
93 | // | ||
94 | // DOUBLE_CLICK (Left mouse button only): | ||
95 | // | ||
96 | // | ||
97 | // SINGLE_CLICK (Left mouse button only): | ||
98 | // If LeftClick selects an anchor point | ||
99 | // append EDIT_ANCHOR mode | ||
100 | // If LeftClick selects a previous point of selected anchor | ||
101 | // append EDIT_PREV mode | ||
102 | // If LeftClick selects a next point of selected anchor | ||
103 | // append EDIT_NEXT mode | ||
104 | // | ||
105 | |||
106 | // ********* Logic for editing ******* | ||
107 | // (update if you change functionality!) | ||
108 | // NOTE: this is out of date...needs to be updated | ||
109 | // | ||
110 | // Start by computing mouse disp | ||
111 | // | ||
112 | // If EDIT_PREV_NEXT mode | ||
113 | // add disp to next and mirror it to prev | ||
114 | // ELSE | ||
115 | // If EDIT_ANCHOR (or _PREV, _NEXT) | ||
116 | // map displacement to anchor (similarly to prev and next) | ||
117 | // | ||
118 | // | ||
119 | |||
120 | |||
121 | ShowToolProperties: { | ||
122 | value: function () { | ||
123 | this._penView = PenView.create(); | ||
124 | this._penView.element = document.getElementById('topPanelContainer').children[0]; | ||
125 | this._penView.needsDraw = true; | ||
126 | |||
127 | this._penView.addEventListener(ToolEvents.TOOL_OPTION_CHANGE, this, false); | ||
128 | } | ||
129 | |||
130 | }, | ||
131 | |||
132 | HandleLeftButtonDown: | ||
133 | { | ||
134 | value: function (event) { | ||
135 | //ignore any right or middle clicks | ||
136 | if (event.button !== 0) { | ||
137 | //NOTE: this will work on Webkit only...IE has different codes (left: 1, middle: 4, right: 2) | ||
138 | return; | ||
139 | } | ||
140 | //BEGIN ShapeTool code | ||
141 | if (this._canDraw) { | ||
142 | this._isDrawing = true; | ||
143 | } | ||
144 | |||
145 | //this._targetedCanvas = stageManagerModule.stageManager.GetObjectFromPoint(event.layerX, event.layerY, this._canOperateOnStage); | ||
146 | |||
147 | this.startDraw(event); | ||
148 | //END ShapeTool code | ||
149 | |||
150 | //assume we are not starting a new path as we will set this to true if we create a new GLSubpath() | ||
151 | this._isNewPath = false; | ||
152 | |||
153 | //add an anchor point by computing position of mouse down | ||
154 | var mouseDownPos = this.getMouseDownPos(); | ||
155 | if (mouseDownPos) { | ||
156 | //if we had closed the selected subpath previously, or if we have not yet started anything, create a subpath | ||
157 | if (this._selectedSubpath === null) { | ||
158 | this._selectedSubpath = new GLSubpath(); | ||
159 | this._isNewPath = true; | ||
160 | if (this._entryEditMode === this.ENTRY_SELECT_PATH){ | ||
161 | //this should not happen, as ENTRY_SELECT_PATH implies there was a selected subpath | ||
162 | this._entryEditMode = this.ENTRY_SELECT_NONE; | ||
163 | } | ||
164 | } else if (this._selectedSubpath.getIsClosed() && this._entryEditMode !== this.ENTRY_SELECT_PATH) { | ||
165 | //since we're not in ENTRY_SELECT_PATH mode, we don't edit the closed path...we start a new path regardless of where we clicked | ||
166 | if (this._makeMultipleSubpaths) { | ||
167 | this._subpaths.push(this._selectedSubpath); | ||
168 | this._penCanvas = null; | ||
169 | this._penPlaneMat = null; | ||
170 | this._snapTarget = null; | ||
171 | this._selectedSubpath = new GLSubpath(); | ||
172 | this._isNewPath = true; | ||
173 | } | ||
174 | } | ||
175 | |||
176 | var prevSelectedAnchorIndex = this._selectedSubpath.getSelectedAnchorIndex(); | ||
177 | // ************* Add/Select Anchor Point ************* | ||
178 | //check if the clicked location is close to an anchor point...if so, make that anchor the selected point and do nothing else | ||
179 | // 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 | ||
180 | var selParam = this._selectedSubpath.pickPath(mouseDownPos[0], mouseDownPos[1], mouseDownPos[2], this._PICK_POINT_RADIUS); | ||
181 | var whichPoint = this._selectedSubpath.getSelectedMode(); | ||
182 | if (whichPoint & this._selectedSubpath.SEL_ANCHOR) { | ||
183 | //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 | ||
184 | if (this._entryEditMode === this.ENTRY_SELECT_PATH && this._isPickedEndPointInSelectPathMode === false){ | ||
185 | var selAnchorIndex = this._selectedSubpath.getSelectedAnchorIndex(); | ||
186 | if (selAnchorIndex===0 || selAnchorIndex===this._selectedSubpath.getNumAnchors()-1){ | ||
187 | //we have picked the endpoint of this path...reverse the path if necessary | ||
188 | if (selAnchorIndex ===0){ | ||
189 | //reverse this path | ||
190 | } | ||
191 | this._isPickedEndPointInSelectPathMode = true; | ||
192 | } | ||
193 | } | ||
194 | this._editMode = this.EDIT_ANCHOR; | ||
195 | //if we have selected the first anchor point, and previously had selected the last anchor point, close the path | ||
196 | var numAnchors = this._selectedSubpath.getNumAnchors(); | ||
197 | if (numAnchors>1 && !this._selectedSubpath.getIsClosed() && this._selectedSubpath.getSelectedAnchorIndex()===0 && prevSelectedAnchorIndex === numAnchors-1){ | ||
198 | //setting the selection mode to NONE will effectively add a new anchor point at the click location and also give us snapping | ||
199 | whichPoint = this._selectedSubpath.SEL_NONE; | ||
200 | //set the snap target in case the mouse move handler doesn't get called | ||
201 | this._snapTarget = this._selectedSubpath.getAnchor(0); | ||
202 | } | ||
203 | } | ||
204 | //check if the clicked location is close to prev and next of the selected anchor point..if so select that anchor, set mode to PREV or NEXT and do nothing else | ||
205 | // but if the selectedAnchor index is not -1 and neither prev nor next are selected, it means click selected a point selParam along bezier segment starting at selectedAnchor | ||
206 | else if (this._selectedSubpath.getSelectedAnchorIndex() !== -1) { | ||
207 | if (whichPoint & this._selectedSubpath.SEL_PREV){ | ||
208 | this._editMode = this.EDIT_PREV; | ||
209 | } | ||
210 | else if (whichPoint & this._selectedSubpath.SEL_NEXT){ | ||
211 | this._editMode = this.EDIT_NEXT; | ||
212 | } | ||
213 | else if (whichPoint & this._selectedSubpath.SEL_PATH) { | ||
214 | //the click point is close enough to insert point in bezier segment after selected anchor at selParam | ||
215 | if (selParam > 0 && selParam < 1) { | ||
216 | this._selectedSubpath.insertAnchorAtParameter(this._selectedSubpath.getSelectedAnchorIndex(), selParam); | ||
217 | //set the mode so that dragging will update anchor point positions | ||
218 | //this._editMode = this.EDIT_ANCHOR; | ||
219 | } | ||
220 | } | ||
221 | } | ||
222 | if (whichPoint === this._selectedSubpath.SEL_NONE) { | ||
223 | if (this._entryEditMode !== this.ENTRY_SELECT_PATH) { | ||
224 | //add an anchor point to end of the subpath, and make it the selected anchor point | ||
225 | if (!this._selectedSubpath.getIsClosed() || this._makeMultipleSubpaths) { | ||
226 | this._selectedSubpath.addAnchor(new GLAnchorPoint()); | ||
227 | var newAnchor = this._selectedSubpath.getAnchor(this._selectedSubpath.getSelectedAnchorIndex()); | ||
228 | newAnchor.setPos(mouseDownPos[0], mouseDownPos[1], mouseDownPos[2]); | ||
229 | newAnchor.setPrevPos(mouseDownPos[0], mouseDownPos[1], mouseDownPos[2]); | ||
230 | newAnchor.setNextPos(mouseDownPos[0], mouseDownPos[1], mouseDownPos[2]); | ||