Skip to content
GitLab
Menu
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
Arch de Noé
CanvaStraight
Commits
d2b31028
Commit
d2b31028
authored
Jul 17, 2021
by
Noé Le Cam
Browse files
Merge branch 'master' of git.unistra.fr:nlecam/canvastraight
parents
97c06869
d5846712
Pipeline
#59055
passed with stage
in 5 seconds
Changes
14
Pipelines
1
Expand all
Hide whitespace changes
Inline
Side-by-side
index.html
View file @
d2b31028
<!DOCTYPE html>
<html
lang=
"en"
>
<head>
<meta
charset=
"UTF-8"
/>
<meta
name=
"viewport"
content=
"width=device-width, initial-scale=1.0"
/>
<link
rel=
"preload"
href=
"test.jpg"
as=
"image"
/>
<link
href=
"stylesheet.css"
rel=
"stylesheet"
/>
<head>
<meta
charset=
"UTF-8"
/>
<meta
name=
"viewport"
content=
"width=device-width, initial-scale=1.0"
/>
<script
src=
"scripts/libs/perspective.js"
></script
>
<script
src=
"scripts/classes/linear_algebra.js"
></script
>
<link
rel=
"preload"
href=
"test.jpg"
as=
"image"
/
>
<link
href=
"stylesheet.css"
rel=
"stylesheet"
/
>
<title>
Perspective Transforms
</title>
</head>
<body>
<main>
<!-- Libraries and utilities -->
<script
src=
"scripts/libs/glfx.js"
></script>
<script
src=
"scripts/libs/linear_algebra.js"
></script>
<title>
Perspective Transforms
</title>
</head>
<body>
<main>
<div>
<canvas
width=
"1280"
height=
"720"
id=
"
test
_canvas"
></canvas>
<div
id=
"controls"
><button
id=
"straighten_btn"
>
Straighten
</button></div>
<canvas
width=
"1280"
height=
"720"
id=
"
2d
_canvas"
></canvas>
<div
id=
"controls"
><button
id=
"straighten_btn"
>
Straighten
</button></div>
</div>
</main>
<script
src=
"scripts/mains.js"
></script>
<script
src=
"scripts/rendering.js"
></script>
<script
src=
"scripts/selection.js"
></script>
<script
src=
"scripts/straighten.js"
></script>
</body>
</html>
</main>
<!-- Scripts -->
<script
src=
"scripts/mains.js"
></script>
<script
src=
"scripts/rendering.js"
></script>
<script
src=
"scripts/selection.js"
></script>
<script
src=
"scripts/straighten.js"
></script>
</body>
</html>
\ No newline at end of file
scripts/deprecated/straighten_deprecated.js
0 → 100644
View file @
d2b31028
/**
* Representation of a segment
*/
class
Segment
{
/**
* Create a segment.
* @param {Vector2D} a - First point
* @param {Vector2D} b - Second point
*/
constructor
(
a
,
b
)
{
this
.
a
=
a
;
this
.
b
=
b
;
}
/**
* Get segments length
*/
get
length
()
{
return
this
.
b
.
sub
(
this
.
a
).
length
;
}
/**
* Returns the 'parallel percentage' to another segment:
* @example 1 : Segments are parallel to eachother
* 0 : Segments are orthogonal
*
* @param {Segment} segment - Segment to compare to
* @returns {number}
*/
parallelFactor
(
segment
)
{
let
seg1
=
this
.
b
.
sub
(
this
.
a
).
normalized
;
let
seg2
=
segment
.
b
.
sub
(
segment
.
a
).
normalized
;
return
seg1
.
dot
(
seg2
);
}
}
// var leftRight = [
// Segment(selection[0], selection[3]),
// Segment(selection[1], selection[2]),
// ];
// var topBottom = [
// Segment(selection[0], selection[3]),
// Segment(selection[1], selection[2]),
// ];
var
rotationMat
=
Matrix2x2
.
rotationMatrix
(
selection
[
3
].
sub
(
selection
[
0
]).
angleTo
(
new
Vector2D
(
1
,
1
))
);
selection
.
forEach
(
(
elmt
,
index
)
=>
(
this
[
index
]
=
rotationMat
.
multiplyVector
(
elmt
)),
selection
);
function
sample
(
x
,
y
,
pixelData
)
{
return
255
;
}
var
button
=
document
.
getElementById
(
"
straighten_btn
"
);
button
.
addEventListener
(
"
click
"
,
function
()
{
window
.
cancelAnimationFrame
(
render2d
);
rendering
=
false
;
console
.
log
(
"
UwU
"
);
let
imgData
=
context2d
.
getImageData
(
0
,
0
,
canvas
.
width
,
canvas
.
height
);
let
screenPixels
=
Array
.
from
(
imgData
.
data
);
for
(
let
i
=
0
;
i
<
imgData
.
data
.
length
;
i
+=
4
)
{
let
x
=
((
i
%
(
canvas
.
width
*
4
))
/
4
);
let
y
=
Math
.
floor
(
i
/
(
canvas
.
width
*
4
));
// imgData.data[i] = screenPixels[pixelIndexY];
// imgData.data[i + 1] = screenPixels[pixelIndexY];
// imgData.data[i + 2] = screenPixels[pixelIndexY];
// imgData.data[i + 3] = 255;
let
pixelIndex
=
x
*
4
+
y
*
canvas
.
width
*
4
;
imgData
.
data
[
i
]
=
screenPixels
[
pixelIndex
];
imgData
.
data
[
i
+
1
]
=
screenPixels
[
pixelIndex
+
1
];
imgData
.
data
[
i
+
2
]
=
screenPixels
[
pixelIndex
+
2
];
imgData
.
data
[
i
+
3
]
=
255
;
// imgData.data[i] = y * 255;
// imgData.data[i + 1] = y * 255;
// imgData.data[i + 2] = y * 255;
// imgData.data[i + 3] = 255;
}
context2d
.
fillStyle
=
"
#000000
"
;
context2d
.
fillRect
(
0
,
0
,
canvas
.
width
,
canvas
.
height
);
context2d
.
putImageData
(
imgData
,
0
,
0
);
});
scripts/deprecated/webgl.js
0 → 100644
View file @
d2b31028
function
setupGl
(
gl
)
{
if
(
gl
===
null
)
{
alert
(
"
Unable to initialize WebGL. Your browser or machine may not support it.
"
);
return
;
}
// Set clear color to black, fully opaque
gl
.
clearColor
(
0.0
,
0.0
,
0.0
,
1.0
);
// Clear the color buffer with specified clear color
gl
.
clear
(
gl
.
COLOR_BUFFER_BIT
);
}
function
createShader
(
gl
,
type
,
source
)
{
var
shader
=
gl
.
createShader
(
type
);
gl
.
shaderSource
(
shader
,
source
);
gl
.
compileShader
(
shader
);
var
success
=
gl
.
getShaderParameter
(
shader
,
gl
.
COMPILE_STATUS
);
if
(
success
)
{
return
shader
;
}
gl
.
deleteShader
(
shader
);
}
function
createProgram
(
gl
,
vertexShader
,
fragmentShader
)
{
var
program
=
gl
.
createProgram
();
gl
.
attachShader
(
program
,
vertexShader
);
gl
.
attachShader
(
program
,
fragmentShader
);
gl
.
linkProgram
(
program
);
var
success
=
gl
.
getProgramParameter
(
program
,
gl
.
LINK_STATUS
);
if
(
success
)
{
return
program
;
}
console
.
log
(
gl
.
getProgramInfoLog
(
program
));
gl
.
deleteProgram
(
program
);
}
setupGl
(
contextGl
);
var
vertexShaderSource
;
async
function
a
()
{
let
u
=
await
readFile
(
"
shaders/vertex_shader_2d.vert
"
);
vertexShaderSource
=
u
;
}
a
();
console
.
log
(
vertexShaderSource
);
var
fragmentShaderSource
=
readFile
(
"
shaders/fragment_shader_2d.frag
"
);
var
vertexShader
=
createShader
(
contextGl
,
contextGl
.
VERTEX_SHADER
,
vertexShaderSource
);
var
fragmentShader
=
createShader
(
contextGl
,
contextGl
.
FRAGMENT_SHADER
,
fragmentShaderSource
);
var
program
=
createProgram
(
contextGl
,
vertexShader
,
fragmentShader
);
var
positionAttributeLocation
=
contextGl
.
getAttribLocation
(
program
,
"
a_position
"
);
var
positionBuffer
=
contextGl
.
createBuffer
();
contextGl
.
bindBuffer
(
contextGl
.
ARRAY_BUFFER
,
positionBuffer
);
var
positions
=
[
0
,
0
,
0
,
0.5
,
0.7
,
0
];
contextGl
.
bufferData
(
contextGl
.
ARRAY_BUFFER
,
new
Float32Array
(
positions
),
contextGl
.
STATIC_DRAW
);
contextGl
.
viewport
(
0
,
0
,
contextGl
.
canvas
.
width
,
contextGl
.
canvas
.
height
);
contextGl
.
clearColor
(
0
,
0
,
0
,
0
);
contextGl
.
clear
(
contextGl
.
COLOR_BUFFER_BIT
);
contextGl
.
useProgram
(
program
);
contextGl
.
enableVertexAttribArray
(
positionAttributeLocation
);
contextGl
.
bindBuffer
(
gl
.
ARRAY_BUFFER
,
positionBuffer
);
// Tell the attribute how to get data out of positionBuffer (ARRAY_BUFFER)
var
size
=
2
;
// 2 components per iteration
var
type
=
gl
.
FLOAT
;
// the data is 32bit floats
var
normalize
=
false
;
// don't normalize the data
var
stride
=
0
;
// 0 = move forward size * sizeof(type) each iteration to get the next position
var
offset
=
0
;
// start at the beginning of the buffer
contextGl
.
vertexAttribPointer
(
positionAttributeLocation
,
size
,
type
,
normalize
,
stride
,
offset
);
var
primitiveType
=
contextGl
.
TRIANGLES
;
var
offset
=
0
;
var
count
=
3
;
contextGl
.
drawArrays
(
primitiveType
,
offset
,
count
);
scripts/libs/glfx.js
0 → 100644
View file @
d2b31028
This diff is collapsed.
Click to expand it.
scripts/
classe
s/linear_algebra.js
→
scripts/
lib
s/linear_algebra.js
View file @
d2b31028
...
...
@@ -35,6 +35,10 @@ class Vector2D {
angleTo
(
vector
)
{
Math
.
acos
(
this
.
dot
(
vector
)
-
this
.
length
*
vector
.
length
);
}
get
asArray
()
{
return
[
this
.
x
,
this
.
y
];
}
}
class
Matrix2x2
{
...
...
scripts/mains.js
View file @
d2b31028
const
canvas
=
document
.
getElementById
(
"
test_canvas
"
);
const
canvas
=
document
.
getElementById
(
"
2d_canvas
"
);
const
fxCanvas
=
fx
.
canvas
();
/** @type {CanvasRenderingcontext2d} */
const
context2d
=
canvas
.
getContext
(
"
2d
"
);
/** @type {WebGLRenderingContext} */
// const contextGl = canvasGl.getContext("webgl");
async
function
readFile
(
path
)
{
let
response
=
await
fetch
(
path
);
return
response
.
text
();
}
var
selection
=
[
new
Vector2D
(
50
,
50
),
new
Vector2D
(
canvas
.
width
-
50
,
50
),
new
Vector2D
(
canvas
.
width
-
50
,
canvas
.
height
-
50
),
new
Vector2D
(
50
,
canvas
.
height
-
50
),
new
Vector2D
(
50
,
50
),
new
Vector2D
(
canvas
.
width
-
50
,
50
),
new
Vector2D
(
canvas
.
width
-
50
,
canvas
.
height
-
50
),
new
Vector2D
(
50
,
canvas
.
height
-
50
),
];
scripts/rendering.js
View file @
d2b31028
var
rendering
=
true
;
var
img
=
new
Image
();
img
.
src
=
"
cat.jpg
"
;
img
.
src
=
"
test.jpg
"
;
// Calculate the height ratio to the canvas
var
ratioToCanvas
=
canvas
.
height
/
img
.
height
;
/**
...
...
@@ -9,35 +10,38 @@ var ratioToCanvas = canvas.height / img.height;
* @param {Vector2D[]} point
*/
function
drawQuad
(
points
)
{
// Styling
context2d
.
lineWidth
=
2
;
context2d
.
strokeStyle
=
"
#ffffff
"
;
context2d
.
fillStyle
=
"
#ffffff
"
;
// Styling
context2d
.
lineWidth
=
2
;
context2d
.
strokeStyle
=
"
#ffffff
"
;
context2d
.
fillStyle
=
"
#ffffff
"
;
// Drawing
context2d
.
beginPath
();
context2d
.
moveTo
(
points
[
3
].
x
,
points
[
3
].
y
);
// Drawing
context2d
.
beginPath
();
context2d
.
moveTo
(
points
[
3
].
x
,
points
[
3
].
y
);
for
(
let
i
=
0
;
i
<
4
;
i
++
)
{
context2d
.
lineTo
(
points
[
i
].
x
,
points
[
i
].
y
);
context2d
.
stroke
();
context2d
.
fillRect
(
points
[
i
].
x
-
10
,
points
[
i
].
y
-
10
,
20
,
20
);
}
for
(
let
i
=
0
;
i
<
4
;
i
++
)
{
context2d
.
lineTo
(
points
[
i
].
x
,
points
[
i
].
y
);
context2d
.
stroke
();
context2d
.
fillRect
(
points
[
i
].
x
-
10
,
points
[
i
].
y
-
10
,
20
,
20
);
}
}
function
render
()
{
if
(
!
rendering
)
return
;
/**
* Render loop
*/
function
render2d
()
{
if
(
!
rendering
)
return
;
context2d
.
clearRect
(
0
,
0
,
canvas
.
width
,
canvas
.
height
);
context2d
.
drawImage
(
img
,
0
,
0
,
img
.
width
*
ratioToCanvas
,
img
.
height
*
ratioToCanvas
);
drawQuad
(
selection
);
requestAnimationFrame
(
render
);
context2d
.
clearRect
(
0
,
0
,
canvas
.
width
,
canvas
.
height
);
context2d
.
drawImage
(
img
,
0
,
0
,
img
.
width
*
ratioToCanvas
,
img
.
height
*
ratioToCanvas
);
drawQuad
(
selection
);
requestAnimationFrame
(
render
2d
);
}
render
();
render
2d
();
\ No newline at end of file
scripts/selection.js
View file @
d2b31028
...
...
@@ -6,49 +6,49 @@ var _grabbedHandle = null;
* @param {Vector2D[]} bounds
*/
function
inBounds
(
point
,
bounds
)
{
return
(
point
.
x
>
bounds
[
0
].
x
&&
point
.
x
<
bounds
[
1
].
x
&&
point
.
y
>
bounds
[
0
].
y
&&
point
.
y
<
bounds
[
1
].
y
);
}
return
(
point
.
x
>
bounds
[
0
].
x
&&
point
.
x
<
bounds
[
1
].
x
&&
point
.
y
>
bounds
[
0
].
y
&&
point
.
y
<
bounds
[
1
].
y
);
}
function
handleMouseDown
(
e
)
{
// Get mouse positon as a Vector2D
let
position
=
new
Vector2D
(
e
.
offsetX
,
e
.
offsetY
);
let
closest
=
{
index
:
0
,
distance
:
selection
[
0
].
sub
(
position
).
lengthSquared
,
};
// Find the closest handle
for
(
let
i
=
1
;
i
<
selection
.
length
;
i
++
)
{
let
distanceToMouse
=
selection
[
i
].
sub
(
position
).
lengthSquared
;
if
(
distanceToMouse
<
closest
.
distance
)
{
closest
.
distance
=
distanceToMouse
;
closest
.
index
=
i
;
}
}
// Compute the bouding box of the handle
let
handleBounds
=
[
selection
[
closest
.
index
].
add
(
new
Vector2D
(
-
10
,
-
10
)),
selection
[
closest
.
index
].
add
(
new
Vector2D
(
10
,
10
)),
];
// Select the handle if mouse in bouding box
if
(
inBounds
(
position
,
handleBounds
))
_grabbedHandle
=
closest
.
index
;
// Get mouse positon as a Vector2D
let
position
=
new
Vector2D
(
e
.
offsetX
,
e
.
offsetY
);
let
closest
=
{
index
:
0
,
distance
:
selection
[
0
].
sub
(
position
).
lengthSquared
,
};
// Find the closest handle
for
(
let
i
=
1
;
i
<
selection
.
length
;
i
++
)
{
let
distanceToMouse
=
selection
[
i
].
sub
(
position
).
lengthSquared
;
if
(
distanceToMouse
<
closest
.
distance
)
{
closest
.
distance
=
distanceToMouse
;
closest
.
index
=
i
;
}
}
// Compute the bouding box of the handle
let
handleBounds
=
[
selection
[
closest
.
index
].
add
(
new
Vector2D
(
-
10
,
-
10
)),
selection
[
closest
.
index
].
add
(
new
Vector2D
(
10
,
10
)),
];
// Select the handle if mouse in bouding box
if
(
inBounds
(
position
,
handleBounds
))
_grabbedHandle
=
closest
.
index
;
}
function
handleMouseUp
(
e
)
{
_grabbedHandle
=
null
;
_grabbedHandle
=
null
;
}
function
handleMouseMove
(
e
)
{
if
(
_grabbedHandle
==
null
)
return
;
// Move the handle according to the mouse
selection
[
_grabbedHandle
]
=
new
Vector2D
(
e
.
offsetX
,
e
.
offsetY
);
if
(
_grabbedHandle
==
null
)
return
;
// Move the handle according to the mouse
selection
[
_grabbedHandle
]
=
new
Vector2D
(
e
.
offsetX
,
e
.
offsetY
);
}
canvas
.
addEventListener
(
"
mousedown
"
,
handleMouseDown
);
...
...
scripts/straighten.js
View file @
d2b31028
var
button
=
document
.
getElementById
(
"
straighten_btn
"
);
/**
*
Representation of a segment
*
On staigthen buton click
*/
class
Segment
{
/**
* Create a segment.
* @param {Vector2D} a - First point
* @param {Vector2D} b - Second point
*/
constructor
(
a
,
b
)
{
this
.
a
=
a
;
this
.
b
=
b
;
}
/**
* Get segments length
*/
get
length
()
{
return
this
.
b
.
sub
(
this
.
a
).
length
;
}
/**
* Returns the 'parallel percentage' to another segment:
* @example 1 : Segments are parallel to eachother
* 0 : Segments are orthogonal
*
* @param {Segment} segment - Segment to compare to
* @returns {number}
*/
parallelFactor
(
segment
)
{
let
seg1
=
this
.
b
.
sub
(
this
.
a
).
normalized
;
let
seg2
=
segment
.
b
.
sub
(
segment
.
a
).
normalized
;
return
seg1
.
dot
(
seg2
);
}
}
// var leftRight = [
// Segment(selection[0], selection[3]),
// Segment(selection[1], selection[2]),
// ];
button
.
addEventListener
(
"
click
"
,
function
()
{
// Stop render loop
window
.
cancelAnimationFrame
(
render2d
);
rendering
=
false
;
// var topBottom = [
// Segment(selection[0], selection[3]),
// Segment(selection[1], selection[2]),
// ];
// Draw the image without gizmo
context2d
.
drawImage
(
img
,
0
,
0
,
img
.
width
*
ratioToCanvas
,
img
.
height
*
ratioToCanvas
);
var
rotationMat
=
Matrix2x2
.
rotationMatrix
(
selection
[
3
].
sub
(
selection
[
0
]).
angleTo
(
new
Vector2D
(
1
,
1
))
);
selection
.
forEach
(
(
elmt
,
index
)
=>
(
this
[
index
]
=
rotationMat
.
multiplyVector
(
elmt
)),
selection
);
// Perspective transform
function
sample
(
x
,
y
,
pixelData
)
{
return
255
;
}
var
texture
=
fxCanvas
.
texture
(
canvas
);
fxCanvas
.
draw
(
texture
);
var
button
=
document
.
getElementById
(
"
straighten_btn
"
);
button
.
addEventListener
(
"
click
"
,
function
()
{
window
.
cancelAnimationFrame
(
render
);
rendering
=
false
;
var
h_t
=
canvas
.
height
;
var
w_t
=
Math
.
floor
(
canvas
.
height
/
(
3
/
4
));
// A4 paper dimensions
let
imgData
=
context2d
.
getImageData
(
0
,
0
,
canvas
.
width
,
canvas
.
height
);
let
screenPixels
=
Array
.
from
(
imgData
.
data
)
;
let
from
=
selection
.
map
((
x
)
=>
x
.
asArray
).
flat
(
);
let
to
=
[
0
,
0
,
w_t
,
0
,
w_t
,
h_t
,
0
,
h_t
]
;
for
(
let
i
=
0
;
i
<
imgData
.
data
.
length
;
i
+=
4
)
{
let
x
=
((
i
%
(
canvas
.
width
*
4
))
/
4
);
let
y
=
Math
.
floor
(
i
/
(
canvas
.
width
*
4
));
// imgData.data[i] = screenPixels[pixelIndexY];
// imgData.data[i + 1] = screenPixels[pixelIndexY];
// imgData.data[i + 2] = screenPixels[pixelIndexY];
// imgData.data[i + 3] = 255;
let
nX
=
x
/
canvas
.
width
;
y
+=
x
;
let
pixelIndex
=
x
*
4
+
y
*
canvas
.
width
*
4
;
fxCanvas
.
perspective
(
from
,
to
);
fxCanvas
.
update
();
imgData
.
data
[
i
]
=
screenPixels
[
pixelIndex
];
imgData
.
data
[
i
+
1
]
=
screenPixels
[
pixelIndex
+
1
];
imgData
.
data
[
i
+
2
]
=
screenPixels
[
pixelIndex
+
2
];
imgData
.
data
[
i
+
3
]
=
255
;
var
trs
=
new
Image
();
trs
.
src
=
fxCanvas
.
toDataURL
(
"
image/png
"
);
// imgData.data[i] = y * 255;
// imgData.data[i + 1] = y * 255;
// imgData.data[i + 2] = y * 255;
// imgData.data[i + 3] = 255;
}
context2d
.
fillStyle
=
"
#000000
"
;
context2d
.
fillRect
(
0
,
0
,
canvas
.
width
,
canvas
.
height
);
context2d
.
putImageData
(
imgData
,
0
,
0
);
trs
.
onload
=
()
=>
{
context2d
.
fillStyle
=
"
#000000
"
;
context2d
.
fillRect
(
0
,
0
,
canvas
.
width
,
canvas
.
height
);
// Draw the resulting image at the center
context2d
.
drawImage
(
trs
,
0
,
0
,
w_t
,
h_t
,
(
canvas
.
width
-
w_t
)
/
2
,
0
,
w_t
,
h_t
);
};
});
shaders/fragment-shader-2d.js
deleted
100644 → 0
View file @
97c06869
shaders/fragment_shader_2d.frag
0 → 100644
View file @
d2b31028
// fragment shaders don't have a default precision so we need
// to pick one. mediump is a good default
precision
mediump
float
;
uniform
sampler2D
float
u_image
;
varying
vec2
v_texCoord
;
void
main
()
{
// gl_FragColor is a special variable a fragment shader
// is responsible for setting
gl_FragColor
=
texture2D
(
u_image
,
v_texCoord
);
// return reddish-purple
}
\ No newline at end of file
shaders/vertex-shader-2d.js
deleted
100644 → 0
View file @
97c06869
shaders/vertex_shader_2d.vert
0 → 100644
View file @
d2b31028
// an attribute will receive data from a buffer
attribute
vec4
a_position
;
varying
vec2
v_texCoord
;