Tracking marker as mouse coordinates in JS (p5js) - javascript

I can scan the marker as I want to, however now I need my code to be able to scan the markers location and treat it as it is my mouseX and mouseY.
// https://kylemcdonald.github.io/cv-examples/
// more here:
// http://fhtr.org/JSARToolKit/demos/tests/test2.html
var capture;
var w = 640,
h = 480;
var raster, param, pmat, resultMat, detector;
function setup() {
pixelDensity(1); // this makes the internal p5 canvas smaller
capture = createCapture({
audio: false,
video: {
width: w,
height: h
}
}, function() {
console.log('capture ready.')
});
capture.elt.setAttribute('playsinline', '');
createCanvas(w, h);
capture.size(w, h);
capture.hide();
raster = new NyARRgbRaster_Canvas2D(canvas);
param = new FLARParam(canvas.width, canvas.height);
pmat = mat4.identity();
param.copyCameraMatrix(pmat, 100, 10000);
resultMat = new NyARTransMatResult();
detector = new FLARMultiIdMarkerDetector(param, 2);
detector.setContinueMode(true);
}
function draw() {
image(capture, 0, 0, w, h);
canvas.changed = true;
var thresholdAmount = 128; //select('#thresholdAmount').value() * 255 / 100;
detected = detector.detectMarkerLite(raster, thresholdAmount);
select('#markersDetected').elt.innerText = detected;
for (var i = 0; i < detected; i++) {
// read data from the marker
// var id = detector.getIdMarkerData(i);
// get the transformation for this marker
detector.getTransformMatrix(i, resultMat);
// convert the transformation to account for our camera
var mat = resultMat;
var cm = mat4.create();
cm[0] = mat.m00, cm[1] = -mat.m10, cm[2] = mat.m20, cm[3] = 0;
cm[4] = mat.m01, cm[5] = -mat.m11, cm[6] = mat.m21, cm[7] = 0;
cm[8] = -mat.m02, cm[9] = mat.m12, cm[10] = -mat.m22, cm[11] = 0;
cm[12] = mat.m03, cm[13] = -mat.m13, cm[14] = mat.m23, cm[15] = 1;
mat4.multiply(pmat, cm, cm);
// define a set of 3d vertices
var q = 1;
var verts = [
vec4.create(-q, -q, 0, 1),
vec4.create(q, -q, 0, 1),
vec4.create(q, q, 0, 1),
vec4.create(-q, q, 0, 1),
// vec4.create(0, 0, -2*q, 1) // poke up
];
// convert that set of vertices from object space to screen space
var w2 = width / 2,
h2 = height / 2;
verts.forEach(function (v) {
mat4.multiplyVec4(cm, v);
v[0] = v[0] * w2 / v[3] + w2;
v[1] = -v[1] * h2 / v[3] + h2;
});
noStroke();
fill(0, millis() % 255);
beginShape();
verts.forEach(function (v) {
vertex(v[0], v[1]);
});
endShape();
}
}
* {
font-family: sans-serif;
}
<html>
<head>
<meta charset="UTF-8">
<title>MarkerTracking</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.6.1/p5.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.6.1/addons/p5.dom.min.js"></script>
<script src="//kig.github.io/JSARToolKit/demos/magi.js"></script>
<script src="//kig.github.io/JSARToolKit/JSARToolKit.js"></script>
<link rel="stylesheet" type="text/css" href="style.css">
</head>
<body>
<h2>cv-examples Marker Tracking source</h2>
<p>Track an AR marker. Take a picture of one of these markers. Make sure it has a white border.</p>
<!-- <p>Threshold: <input type="range" id="thresholdAmount" value="50"></p>-->
<p>Markers detected: <span id="markersDetected"></span></p>
<script src="sketch.js"></script>
</body>
</html>
The snippet might give an error since it is trying to access the camera to scan the marker.
I have tried creating new variables and starting a brand new detection process which didn't work. I am open to any ideas.

Related

P5JS make sure the setup is not cleared but the draw function is on every frame

I'm trying to create a wobbly circle with multiple ellipses in the background, however I can't add the for loop to the draw function otherwise it will be running in every frame, and if I add it to setup then the wobbly circle leaves a trace. And if I use clear() then it removes my background. How can I solve this, please?
function setup() {
createCanvas(600, 600);
for (var i = 0; i < 1000; i++){
fill(255);
ellipse(random(0, width), random(0, width), random(5, 5), random(5, 5));
}
x = width/2; // Circle center x
y = height/2; // Circle center y
r = 100; // Circle radius
verts = 200; // Number of vertices for drawing the circle
wobbleSlider = createSlider(0, 200, 100, 1); // How much the circle radius can vary
smthSlider = createSlider(1, 500, 70, 1); // How smooth the noise function is (higher is smoother)
t = 1;
}
function draw() {
let wobble = wobbleSlider.value()
let smth = smthSlider.value();
//clear();
t += 0.01
beginShape()
for(let i = 0; i < verts; i++){
let f = noise(50*cos(i/verts*2*PI)/smth + t, 50*sin(i/verts*2*PI)/smth + t)
vertex(x + (r+wobble*f) * cos( (i/verts) * 2 * PI ), y + (r+wobble*f) * sin( (i/verts) * 2 * PI ) )
}
endShape(CLOSE)
}
html, body {
margin: 0;
padding: 0;
}
canvas {
display: block;
}
<!DOCTYPE html>
<html lang="en">
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/addons/p5.sound.min.js"></script>
<link rel="stylesheet" type="text/css" href="style.css">
<meta charset="utf-8" />
</head>
<body>
<script src="sketch.js"></script>
</body>
</html>
enter image description here
What you want is some sort of layering system that allows you to have a fixed background and draw over it things that chage. You can achieve this with graphics. It basically allows you to create an independent "canvas" and draw over it, then you can render it like an image in your animation.
First you use createGraphics which is similar to createCanvas with the difference that you can store the result in a variable like: let myGraphics = createGraphics(600,600). Then you draw over it like in your canvas. For example ellipse(...) becomes myGraphics.ellipse(...). Then you can render it using image like image(myGraphics, 0, 0).
See the working code below
If this answers your question dont forget to mark it as the answer using the green check at the left of the answer so others can reference from it in the future :)
let background;
function setup() {
createCanvas(600, 600);
background = createGraphics(600, 400);
for (var i = 0; i < 1000; i++){
background.fill(255);
background.ellipse(random(0, width), random(0, width), random(5, 5), random(5, 5));
}
x = width/2; // Circle center x
y = height/2; // Circle center y
r = 100; // Circle radius
verts = 200; // Number of vertices for drawing the circle
wobbleSlider = createSlider(0, 200, 100, 1); // How much the circle radius can vary
smthSlider = createSlider(1, 500, 70, 1); // How smooth the noise function is (higher is smoother)
t = 1;
}
function draw() {
clear();
image(background,0,0);
let wobble = wobbleSlider.value()
let smth = smthSlider.value();
//clear();
t += 0.01
beginShape()
for(let i = 0; i < verts; i++){
let f = noise(50*cos(i/verts*2*PI)/smth + t, 50*sin(i/verts*2*PI)/smth + t)
vertex(x + (r+wobble*f) * cos( (i/verts) * 2 * PI ), y + (r+wobble*f) * sin( (i/verts) * 2 * PI ) )
}
endShape(CLOSE)
}
html, body {
margin: 0;
padding: 0;
}
canvas {
display: block;
}
<!DOCTYPE html>
<html lang="en">
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/addons/p5.sound.min.js"></script>
<link rel="stylesheet" type="text/css" href="style.css">
<meta charset="utf-8" />
</head>
<body>
<script src="sketch.js"></script>
</body>
</html>

Error displaying an image from localhost on p5js Canvas

Problem:
I am learning p5.js and I am following a tutorial from Coding Train YouTube channel. Everything was fine until I had to call a library function on an Image object. The problem is that I have instantiated the library in an object p and I'm using it's variables through p object. I don't know why it isn't recognizing the loadPixels() function. In the tutorial, the function works fine.
Error Message:
🌸 p5.js says: There's an error as "loadPixels" could not be called as a function
(on line 17 in help.html [file:///G:/Android/help.html:17:11]).
Verify whether "img" has "loadPixels" in it and check the spelling, letter-casing (Javacript is case-sensitive) and its type.
For more: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Errors/Not_a_function#What_went_wrong
Code:
<!DOCTYPE html>
<head>
<script src='p5/p5.js'></script>
</head>
<body>
<div id='container'></div>
<script>
let sketch = function(p) {
p.setup = function(){
p.createCanvas(56, 56);
img = new Image();
img.src = "scott.jpg";
}
p.draw = function() {
// p.drawingContext.drawImage(img, 0, 0);
p.loadPixels();
img.loadPixels();
for (var x=0; x<p.width; x++) {
for (var y=0; y<p.height; y++) {
// var d = p.dist(x, y, p.width/2, p.height/2);
var loc = x+y+p.width;
// p.pixels[loc] = p.color(d);
p.pixels[loc] = img.pixels[loc];
}
}
}
p.updatePixels();
};
new p5(sketch, 'container');
</script>
</body>
</html>
Edit:
As someone pointed out that the problem is that I'm using Image() which is the default Image class for javascript. I did some changes to my code but now it gives me this error.
Error :-
Uncaught DOMException: The operation is insecure. help.html:18
openWindow file:///G:/Android/help.html:18
onclick file:///G:/Android/help.html:1
Code :-
<!DOCTYPE html>
<head>
<script src='p5/p5.js'></script>
</head>
<body>
<button onclick="openWindow()">click me to open a new window.</button>
<div id='container'></div>
<script>
function openWindow() {
var newWindow = window.open("", "Import Image", "height=56,width=56,status=yes,toolbar=no,menubar=no,location=no");
newWindow.document.write("<canvas id='imagePlaceholder'>Canvas not supported!</canvas>");
var canvas = newWindow.document.getElementById("imagePlaceholder");
var ctx = canvas.getContext("2d");
ctx.drawImage(img, 0, 0);
// console.log(ctx.getImageData(0, 0, 56, 56).data);
dest = ctx.getImageData(0, 0, 56, 56).data;
}
let sketch = function(p) {
p.setup = function(){
p.createCanvas(56, 56);
img = new Image();
img.src = "scott.jpg";
let dest = p.createImage(56, 56);
console.log(img);
}
p.draw = function() {
// p.drawingContext.drawImage(img, 0, 0);
// p.loadPixels();
img.loadPixels();
for (var x=0; x<p.width; x++) {
for (var y=0; y<p.height; y++) {
// var d = p.dist(x, y, p.width/2, p.height/2);
var loc = x+y+p.width;
// p.pixels[loc] = p.color(d);
p.pixels[loc] = img.pixels[loc];
}
}
}
p.updatePixels();
};
new p5(sketch, 'container');
</script>
</body>
</html>
Because you are specifically trying to load an image from a local computer as opposed to a publicly accessible URL, a file input with user interaction is going to be the only way to do this. This is a deliberate constraint put in place by web browsers to prevent a malicious webpage from illicitly reading data from your local files. However there is a much simpler way to get the image data from the file input onto your p5js canvas. In fact this exact use case can be seen in the documentation for the createFileInput function.
let input;
let img;
function setup() {
input = createFileInput(handleFile);
input.position(0, 0);
}
function draw() {
background(255);
if (img) {
image(img, 0, 0, width, height);
}
}
function handleFile(file) {
if (file.type === 'image') {
img = createImg(file.data, '');
img.hide();
} else {
img = null;
}
}
<!DOCTYPE html>
<html lang="en">
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.3.1/p5.min.js"></script>
</head>
<body>
</body>
</html>
One obvious problem here is that you are using the builtin Image() constructor, which creates an HTMLImageTag (see the MDN Article) instead of creating a p5js p5.Image object (see the p5js Reference). However there are several other issues. In p5js you need to load images in the preload function to ensure they are available when you start drawing (this is an asynchronous operation). You'd have a much easier time drawing images in p5js using the built in image function. If you are going to use pixel arrays, you need to understand the structure of these arrays. They don't store Color objects, they store 4 separate numbers for each color channel (red, green, blue, and alpha). So the indices in the array are not (x + y * width), but ((x + y * width) * 4 + channel) where channel is a number from 0 to 3. Also you need to account for the fact that the canvas may have a pixel density > 1, whereas the image will have a pixel density of 1. I strongly suggest you read all of the documentation for the Image related p5js functions.
let sketch = function(p) {
let img;
p.preload = function() {
img = p.loadImage("https://s3-ap-southeast-1.amazonaws.com/investingnote-production-webbucket/attachments/41645da792aef1c5054c33de240a52e2c32d205e.png");
};
p.setup = function() {
p.createCanvas(200, 200);
};
p.draw = function() {
// this would be a lot simpler way to draw the image:
// p.image(img, 0, 0);
p.loadPixels();
img.loadPixels();
// Handle high pixel density displays. This code effectively scale the image up so that each 1 pixel in the source image is density * density pixels in the display, thus preserving the size of the image but leading to visible pixelation.
let density = p.pixelDensity();
for (var x = 0; x < p.width && x < img.width; x++) {
for (var y = 0; y < p.height && y < img.height; y++) {
// There are 4 values per pixel in the pixels array:
var srcLoc = (x + y * img.width) * 4;
for (var xd = 0; xd < density; xd++) {
for (var yd = 0; yd < density; yd++) {
var destLoc =
(x * density + xd + (y * density + yd) * p.width * density) * 4;
for (var i = 0; i < 4; i++) {
p.pixels[destLoc + i] = img.pixels[srcLoc + i];
}
}
}
}
}
p.updatePixels();
};
};
new p5(sketch, 'container');
<!DOCTYPE html>
<html lang="en">
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.3.1/p5.min.js"></script>
</head>
<body>
<div id='container'></div>
</body>
</html>
Here is an alternate version of the snippet that handles pixelDensity differently:
let sketch = function(p) {
let img;
p.preload = function() {
img = p.loadImage("https://s3-ap-southeast-1.amazonaws.com/investingnote-production-webbucket/attachments/41645da792aef1c5054c33de240a52e2c32d205e.png");
};
p.setup = function() {
p.createCanvas(200, 200);
};
p.draw = function() {
// this would be a lot simpler way to draw the image:
// p.image(img, 0, 0);
p.loadPixels();
img.loadPixels();
// Handle high pixel density displays. This code shrinks the image down by mapping one pixel in the source image to 1 / (density ^ 2) actual pixels in the canvas.
let density = p.pixelDensity();
for (var x = 0; x < p.width * density && x < img.width; x++) {
for (var y = 0; y < p.height * density && y < img.height; y++) {
// There are 4 values per pixel in the pixels array:
var srcLoc = (x + y * img.width) * 4;
var destLoc = (x + y * p.width * density) * 4;
for (var i = 0; i < 4; i++) {
p.pixels[destLoc + i] = img.pixels[srcLoc + i];
}
}
}
p.updatePixels();
};
};
new p5(sketch, 'container');
<!DOCTYPE html>
<html lang="en">
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.3.1/p5.min.js"></script>
</head>
<body>
<div id='container'></div>
</body>
</html>
I tried a lot of things and almost giveup, but at the end I had to change the code a bit and this worked for me. Although what I got was base64 url as Alice in the comment suggested and I converted it into Uint8ClampedArray. Now if anyone wants a full image or all the pixels of an image then they can follow this link :- https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Pixel_manipulation_with_canvas . I hope it will be helpful for anyone who wants to make an offline webcanvas based application and don't want to dabble with CORS.
var fileSelector = document.createElement('input');
fileSelector.setAttribute('type', 'file');
fileSelector.setAttribute('accept', 'image/gif, image/jpeg, image/png');
fileSelector.click();
fileSelector.onchange = function(e) {
img = new Image();
var file = e.target.files[0];
var reader = new FileReader();
reader.onloadend = function() {
img.src = reader.result;
}
reader.readAsDataURL(file);
var newWindow = window.open("", "_blank", "height=56,width=56,status=yes,toolbar=no,menubar=no,location=no");
newWindow.document.write("<canvas id='imagePlaceholder'>Canvas not supported!</canvas>");
var canvas = newWindow.document.getElementById("imagePlaceholder");
var ctx = canvas.getContext("2d");
ctx.drawImage(img, 0, 0);
// console.log(ctx.getImageData(0, 0, 56, 56).data);
var dest = ctx.getImageData(0, 0, img.width, img.height).data;
console.log(dest);
newWindow.close();
}

How to render a Float32Array cube with vtk.js?

I want to render a 3D volume with vtk.js. My issue is that all the tutorials/examples in the vtk.js documentation load and render vti files. In my case, my data is just a Float32Array which contains the values of a 3D image. How can I volume-render that using vtk.js? I need a solution that creates no additional files on the file system.
I managed (with a coworker's help) to render a random cube volume like this:
// pseudo imports (avoids having to use fully qualified names)
var vtkFullScreenRenderWindow = vtk.Rendering.Misc.vtkFullScreenRenderWindow;
var vtkImageData = vtk.Common.DataModel.vtkImageData;
var vtkDataArray = vtk.Common.Core.vtkDataArray;
var vtkVolume = vtk.Rendering.Core.vtkVolume;
var vtkVolumeMapper = vtk.Rendering.Core.vtkVolumeMapper;
var vtkColorTransferFunction = vtk.Rendering.Core.vtkColorTransferFunction;
var vtkPiecewiseFunction = vtk.Common.DataModel.vtkPiecewiseFunction;
var VtkDataTypes = vtkDataArray.VtkDataTypes;
function initRandomCubeVolume() {
var width = 50, height = 50, depth = 50;
var size = width * height * depth;
var values = [];
for (var i = 0; i < size; i++) {
values[i] = Math.random();
}
var scalars = vtkDataArray.newInstance({
values: values,
numberOfComponents: 1, // number of channels (grayscale)
dataType: VtkDataTypes.FLOAT, // values encoding
name: 'scalars'
});
var imageData = vtkImageData.newInstance();
imageData.setOrigin(0, 0, 0);
imageData.setSpacing(1, 1, 1);
imageData.setExtent(0, width - 1, 0, height - 1, 0, depth - 1);
imageData.getPointData().setScalars(scalars);
var volumeMapper = vtkVolumeMapper.newInstance();
volumeMapper.setInputData(imageData);
volumeMapper.setSampleDistance(0.7);
var volumeActor = vtkVolume.newInstance();
volumeActor.setMapper(volumeMapper);
initProps(volumeActor.getProperty());
var view3d = document.getElementById("view3d");
var fullScreenRenderer = vtkFullScreenRenderWindow.newInstance({
rootContainer: view3d,
containerStyle: {
height: '100%',
overflow: 'hidden'
},
background: [0, 0, 0]
});
var renderer = fullScreenRenderer.getRenderer();
renderer.addVolume(volumeActor);
renderer.resetCamera();
renderer.getActiveCamera().elevation(-70);
renderer.updateLightsGeometryToFollowCamera();
var renderWindow = fullScreenRenderer.getRenderWindow();
renderWindow.render();
}
function initProps(property) {
property.setRGBTransferFunction(0, newColorFunction());
property.setScalarOpacity(0, newOpacityFunction());
property.setScalarOpacityUnitDistance(0, 4.5);
property.setInterpolationTypeToLinear();
property.setUseGradientOpacity(0, true);
property.setGradientOpacityMinimumValue(0, 0, 5);
property.setGradientOpacityMinimumOpacity(0, 0.0);
property.setGradientOpacityMaximumValue(0, 1);
property.setGradientOpacityMaximumOpacity(0, 1.0);
property.setShade(true);
property.setAmbient(0.2);
property.setDiffuse(0.7);
property.setSpecular(0.3);
property.setSpecularPower(8.0);
}
function newColorFunction() {
var fun = vtkColorTransferFunction.newInstance();
fun.addRGBPoint(0, 0.4, 0.2, 0.0);
fun.addRGBPoint(1, 1.0, 1.0, 1.0);
return fun;
}
function newOpacityFunction() {
var fun = vtkPiecewiseFunction.newInstance();
fun.addPoint(0, 0);
fun.addPoint(0.5, 0);
fun.addPoint(0.5, 1);
fun.addPoint(1, 1);
return fun;
}
initRandomCubeVolume();
#view3d { height: calc(100vh - 20px); /* avoid scrollbar because of margins */ }
<script src="https://unpkg.com/vtk.js"></script>
<div id="view3d"></div>
Resistance is futile!

Worst quality perspective image on canvas

I have a problem on my project.
I am developing a perspective mockup creating module for designers. Users upload images and i get them for placing in mockups with making some perspective calculations. Then users can download this image. I made all of this on clientside with js.
But there is a problem for images which are drawn on canvas with perspective calculations like this;
Sample img: http://oi62.tinypic.com/2h49dec.jpg
orginal image size: 6500 x 3592 and you can see spread edges on image...
I tried a few technics like ctx.imageSmoothingEnabled true etc.. But result was always same.
What can i do for solve this problem? What do you think about this?
edit
For more detail;
I get an image (Resolution free) from user then crop it for mockup ratio. For example in my sample image, user image was cropped for imac ratio 16:9 then making calculation with four dot of screen. By the way, my mockup image size is 6500 x 3592. so i made scale, transform etc this cropped image and put it in mockup on canvas. And then use blob to download this image to client...
Thanks.
Solved.
I use perspective.js for calculation on canvas. so I made some revisions on this js source.
If you wanna use or check source;
// Copyright 2010 futomi http://www.html5.jp/
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
// perspective.js v0.0.2
// 2010-08-28
/* -------------------------------------------------------------------
* define objects (name space) for this library.
* ----------------------------------------------------------------- */
if (typeof html5jp == 'undefined') {
html5jp = new Object();
}
(function() {
html5jp.perspective = function(ctxd, image) {
// check the arguments
if (!ctxd || !ctxd.strokeStyle) {
return;
}
if (!image || !image.width || !image.height) {
return;
}
// prepare a <canvas> for the image
var cvso = document.createElement('canvas');
cvso.width = parseInt(image.width) * 2;
cvso.height = parseInt(image.height) * 2;
var ctxo = cvso.getContext('2d');
ctxo.drawImage(image, 0, 0, cvso.width, cvso.height);
// prepare a <canvas> for the transformed image
var cvst = document.createElement('canvas');
cvst.width = ctxd.canvas.width;
cvst.height = ctxd.canvas.height;
var ctxt = cvst.getContext('2d');
ctxt.imageSmoothingEnabled = true;
ctxt.mozImageSmoothingEnabled = true;
ctxt.webkitImageSmoothingEnabled = true;
ctxt.msImageSmoothingEnabled = true;
// parameters
this.p = {
ctxd: ctxd,
cvso: cvso,
ctxo: ctxo,
ctxt: ctxt
}
};
var proto = html5jp.perspective.prototype;
proto.draw = function(points) {
var d0x = points[0][0];
var d0y = points[0][1];
var d1x = points[1][0];
var d1y = points[1][1];
var d2x = points[2][0];
var d2y = points[2][1];
var d3x = points[3][0];
var d3y = points[3][1];
// compute the dimension of each side
var dims = [
Math.sqrt(Math.pow(d0x - d1x, 2) + Math.pow(d0y - d1y, 2)), // top side
Math.sqrt(Math.pow(d1x - d2x, 2) + Math.pow(d1y - d2y, 2)), // right side
Math.sqrt(Math.pow(d2x - d3x, 2) + Math.pow(d2y - d3y, 2)), // bottom side
Math.sqrt(Math.pow(d3x - d0x, 2) + Math.pow(d3y - d0y, 2)) // left side
];
//
var ow = this.p.cvso.width;
var oh = this.p.cvso.height;
// specify the index of which dimension is longest
var base_index = 0;
var max_scale_rate = 0;
var zero_num = 0;
for (var i = 0; i < 4; i++) {
var rate = 0;
if (i % 2) {
rate = dims[i] / ow;
} else {
rate = dims[i] / oh;
}
if (rate > max_scale_rate) {
base_index = i;
max_scale_rate = rate;
}
if (dims[i] == 0) {
zero_num++;
}
}
if (zero_num > 1) {
return;
}
//
var step = 0.10;
var cover_step = step * 250;
//
var ctxo = this.p.ctxo;
var ctxt = this.p.ctxt;
//*** ctxt.clearRect(0, 0, ctxt.canvas.width, ctxt.canvas.height);
if (base_index % 2 == 0) { // top or bottom side
var ctxl = this.create_canvas_context(ow, cover_step);
var cvsl = ctxl.canvas;
for (var y = 0; y < oh; y += step) {
var r = y / oh;
var sx = d0x + (d3x - d0x) * r;
var sy = d0y + (d3y - d0y) * r;
var ex = d1x + (d2x - d1x) * r;
var ey = d1y + (d2y - d1y) * r;
var ag = Math.atan((ey - sy) / (ex - sx));
var sc = Math.sqrt(Math.pow(ex - sx, 2) + Math.pow(ey - sy, 2)) / ow;
ctxl.setTransform(1, 0, 0, 1, 0, -y);
ctxl.drawImage(ctxo.canvas, 0, 0);
//
ctxt.translate(sx, sy);
ctxt.rotate(ag);
ctxt.scale(sc, sc);
ctxt.drawImage(cvsl, 0, 0);
//
ctxt.setTransform(1, 0, 0, 1, 0, 0);
}
} else if (base_index % 2 == 1) { // right or left side
var ctxl = this.create_canvas_context(cover_step, oh);
var cvsl = ctxl.canvas;
for (var x = 0; x < ow; x += step) {
var r = x / ow;
var sx = d0x + (d1x - d0x) * r;
var sy = d0y + (d1y - d0y) * r;
var ex = d3x + (d2x - d3x) * r;
var ey = d3y + (d2y - d3y) * r;
var ag = Math.atan((sx - ex) / (ey - sy));
var sc = Math.sqrt(Math.pow(ex - sx, 2) + Math.pow(ey - sy, 2)) / oh;
ctxl.setTransform(1, 0, 0, 1, -x, 0);
ctxl.drawImage(ctxo.canvas, 0, 0);
//
ctxt.translate(sx, sy);
ctxt.rotate(ag);
ctxt.scale(sc, sc);
ctxt.drawImage(cvsl, 0, 0);
//
ctxt.setTransform(1, 0, 0, 1, 0, 0);
}
}
// set a clipping path and draw the transformed image on the destination canvas.
this.p.ctxd.save();
this.set_clipping_path(this.p.ctxd, [
[d0x, d0y],
[d1x, d1y],
[d2x, d2y],
[d3x, d3y]
]);
this.p.ctxd.drawImage(ctxt.canvas, 0, 0);
this.p.ctxd.restore();
}
proto.create_canvas_context = function(w, h) {
var canvas = document.createElement('canvas');
canvas.width = w;
canvas.height = h;
var ctx = canvas.getContext('2d');
ctx.imageSmoothingEnabled = true;
ctx.mozImageSmoothingEnabled = true;
ctx.webkitImageSmoothingEnabled = true;
ctx.msImageSmoothingEnabled = true;
return ctx;
};
proto.set_clipping_path = function(ctx, points) {
ctx.beginPath();
ctx.moveTo(points[0][0], points[0][1]);
for (var i = 1; i < points.length; i++) {
ctx.lineTo(points[i][0], points[i][1]);
}
ctx.closePath();
ctx.clip();
};
})();
The problem is (most likely, but no code shows so..) that the image is actually too big.
The canvas typically uses bi-linear interpolation (2x2 samples) rather than bi-cubic (4x4 samples). That means if you scale it down a large percentage in one chunk the algorithm will skip some pixels that otherwise should have been sampled, resulting in a more pixelated look.
The solution do is to resize the image in steps, ie. 50% of itself repeatably until a suitable size is achieved. Then use perspective calculations on it. The exact destination size is something you need to find by trial and error, but a good starting point is to use the largest side of the resulting perspective image.
Here is one way to step-down rescale an image in steps.

How do I create circular hotspots with two.js?

I am trying to make a solar system model. I made it all so far using two.js. I want to make it so when the mouse is over a planet's orbit path, it'll highlight that planet's orbit, and be a link to information about that planet. I'm having trouble finding a way to do this, though.
If you don't have it, here's Two.js.
My file structure looks like this:
css
main.css
js
main.js
two.js
index.html
PS. This will not render with the "Run code snippet" feature. If you wish to see it, you must save this code and put it in the same file structure I have.
//This work was created by the minds of Sam Steele and Devin Fowler of Cryptocosm Developers.
//Cryptocosm ©2014
//cryptocosm.x10.bz
//Initiate the render context
var elem = document.getElementById('canvas');
var two = new Two({
fullscreen: true
}).appendTo(elem);
//Define our planets and their colors
var sun = two.makeCircle(0, 0, 70);
var mercury = two.makeCircle(95, 0, 7);
var venus = two.makeCircle(125, 0, 8.5);
var earth = two.makeCircle(160, 0, 11.4);
var mars = two.makeCircle(200, 0, 9.5);
var jupiter = two.makeCircle(260, 0, 28);
// For Saturn we're going to do something special in order to get the rings
var saturnBody = two.makeCircle(320, 0, 24);
var saturnRings = two.makeCurve(296, 0, 290, 10, 322, 10, 350, -8, 342, -10, true);
saturnRings.rotation = 4.5;
var saturn = two.makeGroup(saturnBody, saturnRings);
var uranus = two.makeCircle(460, 0, 18);
var neptune = two.makeCircle(540, 0, 16);
var asteroid = two.makeCircle(0, 320, 3);
//Try to make some stars
var width = window.innerWidth;
var height = window.innerHeight;
var star;
for (i = 0; i < 200; i++) {
var randX = Math.round(Math.random() * width);
var randY = Math.round(Math.random() * height);
star = two.makeCircle(randX, randY, 2);
}
//Set the color of the planets
sun.fill = '#F7CA18';
mercury.fill = '#9E9E9E';
venus.fill = '#795548';
earth.fill = '#2196F3';
mars.fill = '#FF7043';
jupiter.fill = '#E67E22';
saturnBody.fill = '#A1887F';
saturnRings.stroke = "#F5F5F5";
saturnRings.linewidth = 7;
saturnRings.noFill();
saturn.translation.set(20, 0);
uranus.fill = '#4DB6AC';
neptune.fill = '#3F51B5';
star.fill = '#FAFAFA';
asteroid.fill = '#FAFAFA';
//Group the planets
var Mercury = two.makeGroup(mercury);
var Venus = two.makeGroup(venus);
var Earth = two.makeGroup(earth);
var Mars = two.makeGroup(mars);
var Jupiter = two.makeGroup(jupiter);
var Saturn = two.makeGroup(saturn);
var Uranus = two.makeGroup(uranus);
var Neptune = two.makeGroup(neptune);
var planets = two.makeGroup(sun, Mercury, Venus, Earth, Mars, Jupiter, Saturn, Uranus, Neptune);
//Center everything in the center of the element
planets.translation.set(two.width / 2, two.height / 2);
Mercury.rotation = 4;
Venus.rotation = 2.5;
Earth.rotation = 5.5;
Mars.rotation = 1;
Jupiter.rotation = 4.2;
Saturn.rotation = 2.5;
Uranus.rotation = 5.75;
Neptune.rotation = .5;
var counter = document.getElementById('counter');
var count = 0;
var yearsPassed = 0;
// Bind a function to scale and rotate the group to the animation loop.
two.bind('update', function (frameCount) {
//Set the "ZOOM" of the system
planets.scale = .8;
//Rotate all the planets
Mercury.rotation += .01607;
Venus.rotation += .01174;
Earth.rotation += .01;
/* //Earth year counter (not currently accurate at all)
count++;
if (count % 550 == 0) {
yearsPassed++;
counter.innerHTML = "An estimated " + yearsPassed + " Earth years passed";
}; */
Mars.rotation += .00802;
Jupiter.rotation += .00434;
Saturn.rotation += .00323;
Uranus.rotation += .00228;
Neptune.rotation += .00182;
//Rotate Saturn's rings so that it doesn't look dumb
saturnRings.rotation -= .00323;
}).play(); // Finally, start the animation loop
#font-face {
font-family: Roboto-Thin;
font-style: normal;
src: url(Roboto/Roboto-Thin.ttf);
}
html {
background-color: #212121;
}
body {
color: white;
font-family: Roboto-Thin;
}
#counter {
background-color: rgba(0, 0, 0, .4);
width: auto;
height: auto;
font-size: 34px;
float: left;
position: fixed;
padding: 10px;
}
#canvas {}
<html>
<head>
<title>The Solar System</title>
<link href="css/main.css" type="text/css" rel="stylesheet">
<script src="js/two.js"></script>
</head>
<body>
<div id="counter"></div>
<div id="canvas">
</div>
<script src="js/main.js"></script>
</body>
</html>
This is one way to achieve that effect:
This will only work for the svg renderer, but you can add event listeners on mouseover and mouseout as well as touchmove events like so:
// ... After you've created all the planets ...
// This command will create all the dom elements
// which you can bind events to.
two.update();
var highlight = function() { sun.fill = 'red'; };
var ignore = function() { sun.fill = 'yellow'; };
sun._renderer.elem.addEventListener('mousemove', highlight, false);
sun._renderer.elem.addEventListener('mouseout', ignore, false);
There is also an example of how to achieve this here.

Categories

Resources