Webcam stretched when canvas size fixed p5.js - javascript

I am trying to do three things:
Aspect ratio 16:9
Triangle in centre
Webcam on top
I am trying to make it so that different computers can have a triangle in the same position with the total canvas at 16:9, but what currently happens is that it is stretching the video input.
Any ideas?
https://www.openprocessing.org/sketch/870510
var video;
function setup() {
createCanvas(1100, 619);
background(255);
video = createCapture(VIDEO);
video.size(1100,619);
video.hide();
}
function draw() {
image(video,0,0,width,height);
strokeWeight(4);
line(400, 400, 550, 150);
line(550, 150, 700, 400);
line(700, 400, 400, 400);
}

first you need to get window width and then you can calculate the canvas height with 16:9 aspect ratio
var video;
var h, w;
function setup() {
// get window width
w = window.innerWidth;
// calculate canvas height
h = (w * 9) / 16;
// create canvas
createCanvas(w, h);
background(255);
video = createCapture(VIDEO);
// get video scaling ratio
var ratio = h / video.height;
// recalculate video width
var video_w = video.width * ratio;
video.size(video_w, h);
video.hide();
}
and then to make the triangle in center, we can calculate the center by dividing height and width by 2
function draw() {
// calculate center
var cx = w / 2;
var cy = h / 2;
// set the triangle width
var width = 150;
var half_width = width / 2;
// calculate the triangle height using pythagoras theorm
var height = Math.sqrt(width * width - half_width * half_width);
var half_height = height / 2;
// draw bottom of the triangle
line(cx - half_width, cy + half_height, cx + half_width, cy + half_height);
// draw left of the triangle
line(cx - half_width, cy + half_height, cx, cy - half_height);
// draw right of the triangle
line(cx, cy - half_height, cx + half_width, cy + half_height);
}

Related

How to recalculate BoundingBox coordinates from image after scaleToFill transformation in HTML Canvas

I have a boundingBox selecting a zone in a image and i would like to scale this boundingBox to my canvas ratio.
I would like to recalculate the ratio of my boundingBox to correctly target a zone of the resized image in the canvas.
Here an example + jsFiddle : ( this is an example, the real project use multiple boundingBox with a big range of images)``
The boundingBox coordinate / width and height are calculated from the image but after the transformation i dont know how to convert the coordinate / ratio.
const canvas = document.getElementById('canvas')
const ctx = canvas.getContext("2d");
//bbox
let [bx, by, bw, bh] = [146, 58, 82, 87]
console.log(bx, by, bw, bh)
function bboxDraw(){
// Draw the bounding box.
ctx.strokeStyle = "#00FFFF";
ctx.lineWidth = 4;
ctx.strokeRect(bx, by, bw, bh);
// Draw the label background.
ctx.fillStyle = "#00FFFF";
}
function scaleToFill(img, callback){
canvas.width = window.innerWidth
canvas.height = window.innerHeight
// get the scale
var scale = Math.max(canvas.width / img.width, canvas.height / img.height);
// get the top left position of the image
var x = (canvas.width / 2) - (img.width / 2) * scale;
var y = (canvas.height / 2) - (img.height / 2) * scale;
ctx.drawImage(img, x, y, img.width * scale, img.height * scale);
bboxDraw()
}
let img = new Image()
try {
img.src = "https://via.placeholder.com/1280x720"
} catch (error) {
console.log("image URL", error);
}
img.onload = function() {
scaleToFill(this)
}
JSFiddle
Any good idea to preserve the ratio after the scale2fill transformation and correctly target the boundingBox area ?
To recalculate BoundingBox coordinates from image after scale to fill transformation in HTML Canvas.
We need to recalculate the width/height/x/y of the boundingBox using the naturalWidth and naturalHeight of the image.
let [bx, by, bw, bh] = [146, 58, 82, 87]
function bboxRatioDraw(img) {
// Use percent to correctly adapt the coordinate to the scaled image
let percentBx = (100 * (bx / img.naturalWidth)), // x %
percentBy = (100 * (by / img.naturalHeight)), // y %
percentBw = (bw * 100) / img.naturalWidth, // width%
percentBh = (bh * 100) / img.naturalHeight; // height%
// then map the values to the current canvas
let finalBx = (percentBx * canvas.width) / 100, // x en pixel
finalBy = (percentBy * canvas.height) / 100, // y en pixel
finalBw = (percentBw * canvas.width) / 100, // width en pixel
finalBh = (percentBh * canvas.height) / 100; // height en pixel
// Draw the bounding box.
ctx.strokeStyle = "green";
ctx.lineWidth = 4;
ctx.strokeRect(finalBx, finalBy, finalBw, finalBh);
// Draw the label background.
ctx.fillStyle = "#00FFFF";
}
Updated JSFiddle

html5 canvas: auto font size for drawn wrapped rotated text

suppose that there is a text to be drawn inside a rotated bounding rectangle (not aligned to normal axes x-y), and that text can be also rotated,
given the max width of the bounding box, how to select the best font size to use to draw a wrapped text inside that bounding box in html5 canvas and javascript?
I know that method: measureText() can measure dimensions of give font size, but I need the inverse of that: using a known width to get the problem font size.
thanks
You do not have to find the font point size to make it fit. The font will smoothly scale up and down according to the current transformation scale.
All you do is measureText to find its textWidth, get the pointSize from the context.font attribute then if you have the width and height of the box you need to fit then find the minimum of the width / textWidth and height / pointSize and you have the scale that you need to render the font at.
As a function
var scale2FitCurrentFont = function(ctx, text, width, height){
var points, fontWidth;
points = Number(ctx.font.split("px")[0]); // get current point size
points += points * 0.2; // As point size does not include hanging tails and
// other top and bottom extras add 20% to the height
// to accommodate the extra bits
var fontWidth = ctx.measureText(text).width;
// get the max scale that will allow the text to fi the current font
return Math.min(width / fontWidth, height / points);
}
The arguments are
ctx is current context to draw to
text the text to draw
width the width to fit the text to
height the height to fit the text to
Returns the scale to fit the text within the width and height.
The demo has it all integrated and it draws random boxes and fills with random text from your question. It keeps the font selection and point size separate from the font scaling so you can see it will work for any font and any point size.
var demo = function(){
/** fullScreenCanvas.js begin **/
var canvas = (function(){
var canvas = document.getElementById("canv");
if(canvas !== null){
document.body.removeChild(canvas);
}
// creates a blank image with 2d context
canvas = document.createElement("canvas");
canvas.id = "canv";
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
canvas.style.position = "absolute";
canvas.style.top = "0px";
canvas.style.left = "0px";
canvas.style.zIndex = 1000;
canvas.ctx = canvas.getContext("2d");
document.body.appendChild(canvas);
return canvas;
})();
var ctx = canvas.ctx;
/** fullScreenCanvas.js end **/
/** FrameUpdate.js begin **/
var w = canvas.width;
var h = canvas.height;
var cw = w / 2;
var ch = h / 2;
var PI2 = Math.PI * 2; // 360 to save typing
var PIh = Math.PI / 2; // 90
// draws a rounded rectangle path
function roundedRect(ctx,x, y, w, h, r){
ctx.beginPath();
ctx.arc(x + r, y + r, r, PIh * 2, PIh * 3);
ctx.arc(x + w - r, y + r, r, PIh * 3, PI2);
ctx.arc(x + w - r, y + h - r, r, 0, PIh);
ctx.arc(x + r, y + h - r, r, PIh, PIh * 2);
ctx.closePath();
}
// random words
var question = "Suppose that there is a text to be drawn inside a rotated bounding rectangle (not aligned to normal axes x-y), and that text can be also rotated, given the max width of the bounding box, how to select the best font size to use to draw a wrapped text inside that bounding box in html5 canvas and javascript? I know that method: measureText() can measure dimensions of give font size, but I need the inverse of that: using a known width to get the problem font size. thanks.";
question = question.split(" ");
var getRandomWords= function(){
var wordCount, firstWord, s, i, text;
wordCount = Math.floor(rand(4)+1);
firstWord = Math.floor(rand(question.length - wordCount));
text = "";
s = "";
for(i = 0; i < wordCount; i++){
text += s + question[i + firstWord];
s = " ";
}
return text;
}
// fonts to use?? Not sure if these are all safe for all OS's
var fonts = "Arial,Arial Black,Verdanna,Comic Sans MS,Courier New,Lucida Console,Times New Roman".split(",");
// creates a random font with random points size in pixels
var setRandomFont = function(ctx){
var size, font;
size = Math.floor(rand(10, 40));
font = fonts[Math.floor(rand(fonts.length))];
ctx.font = size + "px " + font;
}
var scale2FitCurrentFont = function(ctx, text, width, height){
var points, fontWidth;
var points = Number(ctx.font.split("px")[0]); // get current point size
points += points * 0.2;
var fontWidth = ctx.measureText(text).width;
// get the max scale that will allow the text to fi the current font
return Math.min(width / fontWidth, height / points);
}
var rand = function(min, max){
if(max === undefined){
max = min;
min = 0;
}
return Math.random() * (max - min)+min;
}
var randomBox = function(ctx){
"use strict";
var width, height, rot, dist, x, y, xx, yy,cx, cy, text, fontScale;
// get random box
width = rand(40, 400);
height = rand(10, width * 0.4);
rot = rand(-PIh,PIh);
dist = Math.sqrt(width * width + height * height)
x = rand(0, ctx.canvas.width - dist);
y = rand(0, ctx.canvas.height - dist);
xx = Math.cos(rot);
yy = Math.sin(rot);
ctx.fillStyle = "white";
ctx.strokeStyle = "black";
ctx.lineWidth = 2;
// rotate the box
ctx.setTransform(xx, yy, -yy, xx, x, y);
// draw the box
roundedRect(ctx, 0, 0, width, height, Math.min(width / 3, height / 3));
ctx.fill();
ctx.stroke();
// get some random text
text = getRandomWords();
// get the scale that will fit the font
fontScale = scale2FitCurrentFont(ctx, text, width - textMarginLeftRigth * 2, height - textMarginTopBottom * 2);
// get center of rotated box
cx = x + width / 2 * xx + height / 2 * -yy;
cy = y + width / 2 * yy + height / 2 * xx;
// scale the transform
xx *= fontScale;
yy *= fontScale;
// set the font transformation to fit the box
ctx.setTransform(xx, yy, -yy, xx, cx, cy);
// set up the font render
ctx.fillStyle = "Black";
ctx.textAlign = "center";
ctx.textBaseline = "middle"
// draw the text to fit the box
ctx.fillText(text, 0, 0);
}
var textMarginLeftRigth = 8; // margin for fitted text in pixels
var textMarginTopBottom = 4; // margin for fitted text in pixels
var drawBoxEveryFrame = 60; // frames between drawing new box
var countDown = 1;
// update function will try 60fps but setting will slow this down.
function update(){
// restore transform
ctx.setTransform(1, 0, 0, 1, 0, 0);
// fade clears the screen
ctx.fillStyle = "white"
ctx.globalAlpha = 1/ (drawBoxEveryFrame * 1.5);
ctx.fillRect(0, 0, w, h);
// reset the alpha
ctx.globalAlpha = 1;
// count frames
countDown -= 1;
if(countDown <= 0){ // if frame count 0 the draw another text box
countDown = drawBoxEveryFrame;
setRandomFont(ctx);
randomBox(ctx);
}
if(!STOP){ // do until told to stop.
requestAnimationFrame(update);
}else{
STOP = false;
}
}
update();
}
// demo code to restart on resize
var STOP = false; // flag to tell demo app to stop
function resizeEvent(){
var waitForStopped = function(){
if(!STOP){ // wait for stop to return to false
demo();
return;
}
setTimeout(waitForStopped,200);
}
STOP = true;
setTimeout(waitForStopped,100);
}
window.addEventListener("resize",resizeEvent);
demo();
/** FrameUpdate.js end **/

Getting ugly image while simulating cover in canvas

I'm trying to display the image using cover simulation in canvas. I've found some cool answer on how to do it.
The thing is when I do it with a large picture, it's being displayed ugly. How to fix that?
Here's my Codepen
HTML
<canvas id="canvas"></canvas>
CSS
canvas {
width: 100%;
height: 100vh;
}
JS
var ctx = canvas.getContext('2d'),
img = new Image;
img.onload = draw;
img.src = 'https://upload.wikimedia.org/wikipedia/commons/0/0f/2010-02-19_3000x2000_chicago_skyline.jpg';
function draw() {
drawImageProp(ctx, this, 0, 0, canvas.width, canvas.height);
//drawImageProp(ctx, this, 0, 0, canvas.width, canvas.height, 0.5, 0.5);
}
/**
* By Ken Fyrstenberg
*
* drawImageProp(context, image [, x, y, width, height [,offsetX, offsetY]])
*
* If image and context are only arguments rectangle will equal canvas
*/
function drawImageProp(ctx, img, x, y, w, h, offsetX, offsetY) {
if (arguments.length === 2) {
x = y = 0;
w = ctx.canvas.width;
h = ctx.canvas.height;
}
/// default offset is center
offsetX = offsetX ? offsetX : 0.5;
offsetY = offsetY ? offsetY : 0.5;
/// keep bounds [0.0, 1.0]
if (offsetX < 0) offsetX = 0;
if (offsetY < 0) offsetY = 0;
if (offsetX > 1) offsetX = 1;
if (offsetY > 1) offsetY = 1;
var iw = img.width,
ih = img.height,
r = Math.min(w / iw, h / ih),
nw = iw * r, /// new prop. width
nh = ih * r, /// new prop. height
cx, cy, cw, ch, ar = 1;
/// decide which gap to fill
if (nw < w) ar = w / nw;
if (nh < h) ar = h / nh;
nw *= ar;
nh *= ar;
/// calc source rectangle
cw = iw / (nw / w);
ch = ih / (nh / h);
cx = (iw - cw) * offsetX;
cy = (ih - ch) * offsetY;
/// make sure source rectangle is valid
if (cx < 0) cx = 0;
if (cy < 0) cy = 0;
if (cw > iw) cw = iw;
if (ch > ih) ch = ih;
/// fill image in dest. rectangle
ctx.drawImage(img, cx, cy, cw, ch, x, y, w, h);
}
To accomplish this you could use several techniques like HTML/CSS, CSS Only, Jquery or JS/Canvas. For more on this look here.
You do not have to set the width and height of your canvas in HTML like David Skx mentioned. You do have to erase your CSS, remove it completely.
In your JS you should set your canvas size (just define it in 1 place, don't let different languages interfere):
var canvas = document.getElementById('canvas');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
Window means the whole browser, Use pixels here if you want to limit it to just a canvas and not the whole background
That's all.
You have to specify the width and height in pixels directly on the <canvas>-Element, else it will distort it:
<canvas id="canvas" width="500" height="500"></canvas>
Use JavaScript to measure the window width and height and set it dynamically. Something like:
var canvas = document.getElementById('canvas');
canvas.setAttribute('width', window.innerWidth);
canvas.setAttribute('height', window.innerHeight);
UPDATE:
As Matthijs van Hest pointed out, the width and height attributes on the <canvas>-element are just optional.

Canvas: rotate image with 'canvas'

Rotating image and resize canvas.
var img = document.getElementById("i");
width = img.width;
height = img.height;
canvasH.width = width;
canvasH.height = height;
ctxH.clearRect(0, 0, width, height);
ctxH.save();
ctxH.translate(width / 2, height / 2);
ctxH.rotate(90 * Math.PI / 180);
ctxH.translate(-(width / 2), -(height / 2));
ctxH.drawImage(img, 0, 0);
ctxH.restore();
var w = canvasH.width;
// Resize canvas to meet rotated image size
// Comment last two lines and see how image rotated
canvasH.width = height;
canvasH.height = w;
Canvas rotated (resized) but image out of visible area.
What can I do to get rotated image?
FIDDLE: http://jsfiddle.net/ouh5845c/1/
It is important to know that changing the width/height of canvas implicitly clears the canvas. So, whatever you have to do related to sizing the canvas, do it before rendering to it.
Here's a trigonometrically correct approach, working for any angle:
var img = document.getElementById("i"),
angrad = angle * Math.PI /180,
sin = Math.sin(angrad),
cos = Math.cos(angrad);
width = Math.abs(img.width*cos)+Math.abs(img.height*sin);
height = Math.abs(img.height*cos)+Math.abs(img.width*sin);
console.log(img.width,img.height,width,height);
canvasH.width = width;
canvasH.height = height;
ctxH.clearRect(0, 0, width, height);
ctxH.save();
ctxH.translate(width / 2, height / 2);
ctxH.rotate(angrad);
ctxH.translate(-img.width / 2, -img.height / 2);
ctxH.drawImage(img, 0, 0);
ctxH.restore();
http://jsfiddle.net/ouh5845c/5/
===========================
and the following story you can forget:
Now, because you need to rotate, width and height are interpreted differently before rotation and after rotation. (Of course, for a "hairy" situation when the angle is not 90 degrees, some trigonometry would come in handy).
I believe this fiddle does what you needed:
var img = document.getElementById("i");
width = img.width;
height = img.height;
canvasH.width = height;
canvasH.height = width;
ctxH.clearRect(0, 0, width, height);
ctxH.save();
ctxH.translate(height / 2, width / 2);
ctxH.rotate(90 * Math.PI / 180);
ctxH.translate(-width / 2, -height / 2);
ctxH.drawImage(img, 0, 0);
ctxH.restore();
http://jsfiddle.net/ouh5845c/4/
If you are trying to rotate the image 90 degrees this should work.
var canvasH = document.getElementById("canvasH"),
ctxH = canvasH.getContext("2d"),
x = 0,
y = 0,
width = 0,
height = 0,
angle = 180,
timeOut = null;
function loaded() {
var img = document.getElementById("i");
canvasH.width = img.height;
canvasH.height = img.width;
ctxH.clearRect(0, 0, img.width, img.height);
ctxH.save();
ctxH.translate(img.height, 0);
ctxH.rotate(1.57079633);
ctxH.drawImage(img, 0, 0);
ctxH.restore();
}
jsFiddle

KineticsJs auto resize canvas

My autoresize canvas not works with kineticjs, the console say me that canvas.width and canvas.height is not defined.
Any people could help me.
Thank you .
Put the function and the call down.
function resize() {
console.log("ddddddddddddd");
//stage.setWidth((window.innerWidth / 100) * 80);
var canvas = document.getElementById('Contenedor');
console.log(canvas, canvas.height, canvas.width);
if(window.innerWidth < 1280){
var canvasRatio = canvas.height / canvas.width;
console.log(canvasRatio);
var windowRatio = window.innerHeight / window.innerWidth;
console.log(windowRatio);
var width;
var height;
if (windowRatio < canvasRatio) {
height = window.innerHeight;
width = height / canvasRatio;
} else {
width = window.innerWidth;
height = width * canvasRatio;
}
canvas.style.width = (parseFloat(width)*0.75) + 'px';
canvas.style.height = (parseFloat(height)*0.75) + 'px';
}else{
canvas.style.width=987+'px';
canvas.style.height=544+'px';
}
};
window.addEventListener('resize', resize, false);
What you can do is set the height of the canvas with respect to the width.
And use stage.setScale() and stage.setSize() to dynamically change the size of the stage
function resize() {
var canvasWidthRatio = 0.6; // canvas width to viewport width ratio
var viewPortWidth = window.innerWidth;
var canvasWidth = viewPortWidth * canvasWidthRatio;
var canvasHeight = canvasWidth * 0.6,
scale = stage.getScale(),
stageWidth = stage.getWidth(),
stageHeight = stage.getHeight(),
scaleFactor = Math.min(canvasWidth / stageWidth, canvasHeight / stageHeight),
newScale = scale * scaleFactor;
stage.setScale(newScale);
stage.setSize(canvasWidth, canvasHeight);
stage.draw();
}
window.addEventListener('resize', resize, false);
I think kineticjs sets the canvas dimensions.
set the stage width and height and don't touch the canvas directly.
Hello and sorry my bad english, also thank you to help me. I work with the code of VijayB, but with his code I have problems with multiply the object scale with a float.. I realease a few changes and for me are working.
I put the code for someone that have my own problem.
function resize() {
var __canvasWidthRatio = 0.6;
console.log( __canvasWidthRatio) ; // canvas width to viewport width ratio
var __viewPortWidth = window.innerWidth;
console.log(window.innerWidth);
var __canvasWidth = __viewPortWidth * __canvasWidthRatio;
console.log(__canvasWidth);
var aspec = 769 / 544;
var __canvasHeight = aspec *__canvasWidth * __canvasWidthRatio;
var __scale = ui.stage.getScale();
var __stageWidth = ui.stage.getWidth();
var __stageHeight = ui.stage.getHeight();
var __scaleFactor = Math.min( __canvasWidth / __stageWidth, __canvasHeight / __stageHeight);
stage.escala= __scale.x *__scaleFactor;
ui.scale=__scale.x *__scaleFactor;
stage.setScale(stage.escala,stage.escala);
stage.setSize(__canvasWidth, __canvasHeight);
stage.draw();
}
window.addEventListener('resize', resize, false);

Categories

Resources