├── .gitignore
├── img
├── screenPos-Cover.png
└── screenPos-Cover-small.png
├── LICENSE
├── examples
├── 01-basic.html
├── 03-reset-apply.html
├── 02-instance-mode.html
└── 04-createGraphics.html
├── README.md
└── addScreenPositionFunction.js
/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | *.ai
3 |
--------------------------------------------------------------------------------
/img/screenPos-Cover.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bohnacker/p5js-screenPosition/HEAD/img/screenPos-Cover.png
--------------------------------------------------------------------------------
/img/screenPos-Cover-small.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bohnacker/p5js-screenPosition/HEAD/img/screenPos-Cover-small.png
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Hartmut Bohnacker
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/examples/01-basic.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | p5.js example
7 |
8 |
9 |
10 |
11 |
12 |
13 |
71 |
72 |
73 |
--------------------------------------------------------------------------------
/examples/03-reset-apply.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | p5.js example
7 |
8 |
9 |
10 |
11 |
12 |
13 |
70 |
71 |
72 |
--------------------------------------------------------------------------------
/examples/02-instance-mode.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | p5.js example
7 |
8 |
9 |
10 |
11 |
12 |
13 |
72 |
73 |
74 |
--------------------------------------------------------------------------------
/examples/04-createGraphics.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | p5.js example
7 |
8 |
9 |
10 |
11 |
12 |
13 |
65 |
66 |
67 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # p5js-screenPosition
2 |
3 | 
4 |
5 | This function provides a solution to the missing screenX and screenY functions in p5.js. See https://github.com/processing/p5.js/issues/1553 for the discussion.
6 |
7 | **This is work in progress.**
8 |
9 | As far as I could find out there should be anything implemented to make it work perfectly in any mode (2D, WEBGL, angleMode DEGREES or RADIANS, normal or instance mode). If you'll find a case that isn't working correctly, please file an issue.
10 |
11 | ## Usage
12 |
13 | For a quick look on what it's doing see https://editor.p5js.org/bohnacker/sketches/nUk3bVW7b.
14 |
15 | ### 1. Download
16 | Download the latest release of `addScreenPositionFunction.js`.
17 |
18 | ### 2. Use it in your project
19 | Link to it in your HTML file. It's only working in combination with p5.js.
20 |
21 | ``` html
22 |
23 |
24 | ```
25 |
26 | ### 3. Initializing
27 | Call the function `addScreenPositionFunction()` after creating the canvas.
28 |
29 | ``` javascript
30 | function setup() {
31 | createCanvas(400, 400);
32 | addScreenPositionFunction();
33 | }
34 | ```
35 |
36 | If your using p5.js in instance mode you have to pass the instance:
37 |
38 | ``` javascript
39 | p.setup = function() {
40 | p.createCanvas(400, 400);
41 | addScreenPositionFunction( p );
42 | };
43 | ```
44 |
45 | Quite similar if you want to use the function with offscreen graphics:
46 |
47 | ``` javascript
48 | graphics = createGraphics(400, 400);
49 | addScreenPositionFunction( graphics );
50 | ```
51 |
52 |
53 | ### 4. Getting the screen position
54 | Now you can use `screenPosition(x, y, [z])` to get the position of a coordinate on screen. It returns a p5.Vector.
55 | ``` javascript
56 | var p = screenPosition(-100, 50, 0);
57 | ```
58 |
59 | Or giving a p5.Vector:
60 | ``` javascript
61 | var v = createVector(-100, 50, 0)
62 | var p = screenPosition( v );
63 | ```
64 |
65 | Or giving an array with x, y, z coordinates:
66 | ``` javascript
67 | var v = [-100, 50, 0];
68 | var p = screenPosition( v );
69 | ```
70 |
71 | ## Acknowledgements
72 | Thanks to Thibault Coppex (@tcoppex) for the 3d-modelview-projection-math he supplied in the issue discussion thread (https://github.com/processing/p5.js/issues/1553). I had to adjust it a bit maybe because p5js changed the way webgl is handled since 2016.
73 |
--------------------------------------------------------------------------------
/addScreenPositionFunction.js:
--------------------------------------------------------------------------------
1 | // Acknowledgement to Thibault Coppex (@tcoppex) for the 3d-modelview-projection-math.
2 | // Had to adjust it a bit maybe because p5js changed the way webgl is handled since 2016.
3 |
4 | // See: https://editor.p5js.org/bohnacker/sketches/nUk3bVW7b on how to use it
5 |
6 |
7 | function addScreenPositionFunction(p5Instance) {
8 | let p = p5Instance || this;
9 |
10 | // find out which context we're in (2D or WEBGL)
11 | const R_2D = 0;
12 | const R_WEBGL = 1;
13 | let context = getObjectName(p._renderer.drawingContext).search("2D") >= 0 ? R_2D : R_WEBGL;
14 |
15 | // the stack to keep track of matrices when using push and pop
16 | if (context == R_2D) {
17 | p._renderer.matrixStack = [new p5.Matrix()];
18 | }
19 |
20 | // replace all necessary functions to keep track of transformations
21 |
22 | if (p.draw instanceof Function) {
23 | let drawNative = p.draw;
24 | p.draw = function(...args) {
25 | if (context == R_2D) p._renderer.matrixStack = [new p5.Matrix()];
26 | drawNative.apply(p, args);
27 | };
28 | }
29 |
30 |
31 | if (p.resetMatrix instanceof Function) {
32 | let resetMatrixNative = p.resetMatrix;
33 | p.resetMatrix = function(...args) {
34 | if (context == R_2D) p._renderer.matrixStack = [new p5.Matrix()];
35 | resetMatrixNative.apply(p, args);
36 | };
37 | }
38 |
39 | if (p.translate instanceof Function) {
40 | let translateNative = p.translate;
41 | p.translate = function(...args) {
42 | if (context == R_2D) last(p._renderer.matrixStack).translate(args);
43 | translateNative.apply(p, args);
44 | };
45 | }
46 |
47 | if (p.rotate instanceof Function) {
48 | let rotateNative = p.rotate;
49 | p.rotate = function(...args) {
50 | if (context == R_2D) {
51 | let rad = p._toRadians(args[0]);
52 | last(p._renderer.matrixStack).rotateZ(rad);
53 | }
54 | rotateNative.apply(p, args);
55 | };
56 | }
57 |
58 | if (p.rotateX instanceof Function) {
59 | let rotateXNative = p.rotateX;
60 | p.rotateX = function(...args) {
61 | if (context == R_2D) {
62 | let rad = p._toRadians(args[0]);
63 | last(p._renderer.matrixStack).rotateX(rad);
64 | }
65 | rotateXNative.apply(p, args);
66 | };
67 | }
68 | if (p.rotateY instanceof Function) {
69 | let rotateYNative = p.rotateY;
70 | p.rotateY = function(...args) {
71 | if (context == R_2D) {
72 | let rad = p._toRadians(args[0]);
73 | last(p._renderer.matrixStack).rotateY(rad);
74 | }
75 | rotateYNative.apply(p, args);
76 | };
77 | }
78 | if (p.rotateZ instanceof Function) {
79 | let rotateZNative = p.rotateZ;
80 | p.rotateZ = function(...args) {
81 | if (context == R_2D) {
82 | let rad = p._toRadians(args[0]);
83 | last(p._renderer.matrixStack).rotateZ(rad);
84 | }
85 | rotateZNative.apply(p, args);
86 | };
87 | }
88 |
89 | if (p.scale instanceof Function) {
90 | let scaleNative = p.scale;
91 | p.scale = function(...args) {
92 | if (context == R_2D) {
93 | let m = last(p._renderer.matrixStack);
94 | let sx = args[0];
95 | let sy = args[1] || sx;
96 | let sz = context == R_2D ? 1 : args[2];
97 | m.scale([sx, sy, sz]);
98 | }
99 | scaleNative.apply(p, args);
100 | };
101 | }
102 |
103 | // Help needed: don't know what transformation matrix to use
104 | // Solved: Matrix multiplication had to be in reversed order.
105 | // Still, this looks like it could be simplified.
106 |
107 | if (p.shearX instanceof Function) {
108 | let shearXNative = p.shearX;
109 | p.shearX = function(...args) {
110 | if (context == R_2D) {
111 | let rad = p._toRadians(args[0]);
112 | let stack = p._renderer.matrixStack;
113 | let m = last(stack);
114 | let sm = new p5.Matrix();
115 | sm.mat4[4] = Math.tan(rad);
116 | sm.mult(m);
117 | stack[stack.length - 1] = sm;
118 | }
119 | shearXNative.apply(p, args);
120 | };
121 | }
122 |
123 | if (p.shearY instanceof Function) {
124 | let shearYNative = p.shearY;
125 | p.shearY = function(...args) {
126 | if (context == R_2D) {
127 | let rad = p._toRadians(args[0]);
128 | let stack = p._renderer.matrixStack;
129 | let m = last(stack);
130 | let sm = new p5.Matrix();
131 | sm.mat4[1] = Math.tan(rad);
132 | sm.mult(m);
133 | stack[stack.length - 1] = sm;
134 | }
135 | shearYNative.apply(p, args);
136 | };
137 | }
138 |
139 |
140 | if (p.applyMatrix instanceof Function) {
141 | let applyMatrixNative = p.applyMatrix;
142 | p.applyMatrix = function(...args) {
143 | if (context == R_2D) {
144 | let stack = p._renderer.matrixStack;
145 | let m = last(stack);
146 | let sm = new p5.Matrix();
147 | sm.mat4[0] = args[0];
148 | sm.mat4[1] = args[1];
149 | sm.mat4[4] = args[2];
150 | sm.mat4[5] = args[3];
151 | sm.mat4[12] = args[4];
152 | sm.mat4[13] = args[5];
153 | sm.mult(m);
154 | stack[stack.length - 1] = sm;
155 | }
156 | applyMatrixNative.apply(p, args);
157 | };
158 | }
159 |
160 |
161 | if (p.push instanceof Function) {
162 | let pushNative = p.push;
163 | p.push = function(...args) {
164 | if (context == R_2D) {
165 | let m = last(p._renderer.matrixStack);
166 | p._renderer.matrixStack.push(m.copy());
167 | }
168 | pushNative.apply(p, args);
169 | };
170 | }
171 | if (p.pop instanceof Function) {
172 | let popNative = p.pop;
173 | p.pop = function(...args) {
174 | if (context == R_2D) p._renderer.matrixStack.pop();
175 | popNative.apply(p, args);
176 | };
177 | }
178 |
179 |
180 |
181 | p.screenPosition = function(x, y, z) {
182 | if (x instanceof p5.Vector) {
183 | let v = x;
184 | x = v.x;
185 | y = v.y;
186 | z = v.z;
187 | } else if (x instanceof Array) {
188 | let rg = x;
189 | x = rg[0];
190 | y = rg[1];
191 | z = rg[2] || 0;
192 | }
193 | z = z || 0;
194 |
195 | if (context == R_2D) {
196 | let m = last(p._renderer.matrixStack);
197 | // probably not needed:
198 | // let mInv = (new p5.Matrix()).invert(m);
199 |
200 | let v = p.createVector(x, y, z);
201 | let vCanvas = multMatrixVector(m, v);
202 | // console.log(vCanvas);
203 | return vCanvas;
204 |
205 | } else {
206 | let v = p.createVector(x, y, z);
207 |
208 | // Calculate the ModelViewProjection Matrix.
209 | let mvp = (p._renderer.uMVMatrix.copy()).mult(p._renderer.uPMatrix);
210 |
211 | // Transform the vector to Normalized Device Coordinate.
212 | let vNDC = multMatrixVector(mvp, v);
213 |
214 | // Transform vector from NDC to Canvas coordinates.
215 | let vCanvas = p.createVector();
216 | vCanvas.x = 0.5 * vNDC.x * p.width;
217 | vCanvas.y = 0.5 * -vNDC.y * p.height;
218 | vCanvas.z = 0;
219 |
220 | return vCanvas;
221 | }
222 |
223 | }
224 |
225 |
226 | // helper functions ---------------------------
227 |
228 | function last(arr) {
229 | return arr[arr.length - 1];
230 | }
231 |
232 | function getObjectName(obj) {
233 | var funcNameRegex = /function (.{1,})\(/;
234 | var results = (funcNameRegex).exec((obj).constructor.toString());
235 | return (results && results.length > 1) ? results[1] : "";
236 | };
237 |
238 |
239 | /* Multiply a 4x4 homogeneous matrix by a Vector4 considered as point
240 | * (ie, subject to translation). */
241 | function multMatrixVector(m, v) {
242 | if (!(m instanceof p5.Matrix) || !(v instanceof p5.Vector)) {
243 | print('multMatrixVector : Invalid arguments');
244 | return;
245 | }
246 |
247 | var _dest = p.createVector();
248 | var mat = m.mat4;
249 |
250 | // Multiply in column major order.
251 | _dest.x = mat[0] * v.x + mat[4] * v.y + mat[8] * v.z + mat[12];
252 | _dest.y = mat[1] * v.x + mat[5] * v.y + mat[9] * v.z + mat[13];
253 | _dest.z = mat[2] * v.x + mat[6] * v.y + mat[10] * v.z + mat[14];
254 | var w = mat[3] * v.x + mat[7] * v.y + mat[11] * v.z + mat[15];
255 |
256 | if (Math.abs(w) > Number.EPSILON) {
257 | _dest.mult(1.0 / w);
258 | }
259 |
260 | return _dest;
261 | }
262 |
263 | }
--------------------------------------------------------------------------------