Different behavior of matter.js-animated objects in Firefox and Chrome (animation) - google-chrome

I have this fairly simple physics simulation where I animate the left / right positions using matter.js. it's online, under "every.bot".
The code that does it is
const { Engine, Render, Runner, World, Bodies } = Matter;
const engine = Matter.Engine.create({enableSleeping: true});
const iconWidth = 120
// Create 3 HTML elements and add them to the canvas
const element1 = document.createElement("img");
element1.src = "IMG/icon_it.png"
element1.style.width = `${iconWidth}px`
element1.style.height = `${iconWidth}px`
element1.style.position = "absolute"
document.getElementById("canvas").appendChild(element1)
const element2 = document.createElement("img");
element2.src = "IMG/icon_webdev.png";
element2.style.width = `${iconWidth}px`
element2.style.height = `${iconWidth}px`
element2.style.position = "absolute";
document.getElementById("canvas").appendChild(element2);
const element3 = document.createElement("img");
element3.src = "IMG/icon_3d.png";
element3.style.width = `${iconWidth}px`
element3.style.height = `${iconWidth}px`
element3.style.position = "absolute";
document.getElementById("canvas").appendChild(element3);
const body = document.getElementById("body")
const base = Matter.Bodies.rectangle(body.offsetWidth, canvas.offsetHeight, body.offsetWidth*2, 6, {
isStatic: true
})
const wallL = Matter.Bodies.rectangle(0, canvas.offsetHeight, 6, body.offsetHeight, {
isStatic: true
})
const wallR = Matter.Bodies.rectangle(body.offsetWidth, canvas.offsetHeight, 6, body.offsetHeight, {
isStatic: true
})
console.log(body.offsetWidth)
// Create 3 matter.js bodies for the HTML elements and add them to the engine
const body1 = Matter.Bodies.rectangle(((canvas.offsetWidth/2)-iconWidth/2)+Math.floor(Math.random() * 241) - 120, 0, iconWidth, iconWidth);
const body2 = Matter.Bodies.rectangle(((canvas.offsetWidth/2)-iconWidth/2)+Math.floor(Math.random() * 241) - 120, 150, iconWidth, iconWidth);
const body3 = Matter.Bodies.rectangle((canvas.offsetWidth/2)-iconWidth/2, 300, iconWidth, iconWidth);
const friction = 0.9
const airFriction = 0.5
body1.friction = friction
body2.friction = friction
body3.friction = friction
body1.airFriction = airFriction
body2.airFriction = airFriction
body3.airFriction = airFriction
Matter.World.add(engine.world, [body1,body2,body3,base,wallR,wallL]);
// Run the engine
Matter.Runner.run(engine);
// Update the position of the HTML elements based on their matter.js bodies
Matter.Events.on(engine, "afterUpdate", function() {
const element1PosX = body1.position.x-iconWidth/2
const element2PosX = body2.position.x-iconWidth/2
const element3PosX = body3.position.x-iconWidth/2
const element1PosY = body1.position.y-iconWidth/2
const element2PosY = body2.position.y-iconWidth/2
const element3PosY = body3.position.y-iconWidth/2
element1.style.left = element1PosX + "px";
element1.style.top = element1PosY + "px";
element2.style.left = element2PosX + "px";
element2.style.top = element2PosY + "px";
element3.style.left = element3PosX + "px";
element3.style.top = element3PosY + "px";
});
I know I shoud DRY up this bunch, but I'm almost afraid to lose the bug that I'm about to report, which is absolutely blowing my mind:
The objects behave almost normal on Chrome in Full-HD. But when I set the Browser to fullscreen (F11), they start bouncing around after impacts, oscillating weirdly.
This in itself is strange. But what's even weirder is that the behavior on FF is the complete opposite! If I set it to fullscreen, objects behave normally. In normal mode, they bounce around like in Chrome, fullscreen mode.
I'm on PC. Have I found a glitch in the matrix? Don't tell me I need to understand the inner workings of browsers just to use a physics sim.... 🤦‍♂️

Related

Why when hovering sprite on DataVisualization the Card Position not correctly in center?

I try to perform a Sprite using DataViz Extension. However the position of the Info Card is not correct when I zoom it.
Here is my Code
onSpriteHovering(event) {
const hovered = event.hovering;
if (hovered) {
const firstDbId = event.dbId;
const model = event.target.model;
const instanceTree = model.getData().instanceTree;
const fragList = model.getFragmentList();
let bounds = new THREE.Box3();
instanceTree.enumNodeFragments(
firstDbId,
(fragId) => {
let box = new THREE.Box3();
fragList.getWorldBounds(fragId, box);
bounds.union(box);
},
true
);
this.posModel = bounds.getCenter();
const dataSprite = {
id: firstDbId,
};
this.infoChart.showIcon(firstDbId, dataSprite, this.posModel);
// this.infoChart.makeChart();
} else {
this.infoChart.clearInfoChart();
}
}
Updating Position :
updateIcons() {
if (this.infoChart && this.posModel) {
const pos = this.viewer.worldToClient(this.posModel);
this.infoChart.style.left = `${Math.floor(
100 + pos.x - this.infoChart.offsetWidth / 2
)}px`;
this.infoChart.style.top = `${Math.floor(
100 + pos.y - this.infoChart.offsetWidth / 2
)}px`;
}
}

How to check if the model out of the viewer container?

I made an info card and this card will disappear if the viewer is rotated until the model is not visible. I use isNodevisible but it always returns true.
updateInfoCard() {
if (this.infoCard && this.posModel) {
const pos = this.viewer.worldToClient(this.posModel);
console.log(pos);
this.infoCard.style.left = `${Math.floor(
50 + pos.x - this.infoCard.offsetWidth / 2
)}px`;
this.infoCard.style.top = `${Math.floor(
50 + pos.y - this.infoCard.offsetWidth / 2
)}px`;
const id = this.infoCard.dataset.id;
console.log(this.viewer.isNodeVisible(id));
this.infoCard.style.display = this.viewer.isNodeVisible(id)
? "block"
: "none";
}
}
If I understand your question correctly, you'll probably want to do an intersection test between the camera's frustum and the models's bounding box. That can be done like so:
viewer.addEventListener(Autodesk.Viewing.CAMERA_CHANGE_EVENT, function () {
if (!viewer.model) {
return;
}
const camera = viewer.getCamera();
const matrix = new THREE.Matrix4().multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse);
const frustum = new THREE.Frustum().setFromMatrix(matrix);
const bbox = viewer.model.getBoundingBox();
console.log('Model in the view?', frustum.intersectsBox(bbox));
});
And if you only want to check the visibility of a specific element (based on its dbID) of your model, you can compute its bounding box like so:
function objectBounds(model, dbId) {
const tree = model.getInstanceTree();
const frags = model.getFragmentList();
const objectBounds = new THREE.Box3();
tree.enumNodeFragments(dbId, function (fragId) {
const fragBounds = new THREE.Box3();
frags.getWorldBounds(fragId, fragBounds);
objectBounds.union(fragBounds);
}, true);
return objectBounds;
}
The function isNodeVisible returns the visibility status of your node in the scene. If you do something like this.viewer.hide(id, model) your function will return false.
If I well understood what you want to achieve, you want to hide an info card when the associated object is occluded by others objects, so we can't see it from our point of view ?
So I think what you need is to check for occlusion. You can take a look at the checkOcclusion function of this point cloud markup extension made by Philippe Leefsma.
To check for node occlusion, you basically need to raycast from your point of view to the node that you want to check. If you hit something and it's your node, there is no occlusion. If it's not the same node, it's mean that something occlude your node.
checkOcclusion (markup) {
const clientPoint = this.viewer.worldToClient(
markup.point)
const offset = $(this.viewer.container).offset()
const rayCaster = this.pointToRaycaster(
this.viewer.impl.canvas,
this.viewer.impl.camera, {
x: clientPoint.x + offset.left,
y: clientPoint.y + offset.top
})
const hitTest = this.viewer.model.rayIntersect(
rayCaster, true, this.dbIds)
if (hitTest) {
if (hitTest.fragId === markup.fragId) {
const offset = {
x: hitTest.point.x - markup.point.x,
y: hitTest.point.y - markup.point.y,
z: hitTest.point.z - markup.point.z
}
const dist = Math.sqrt(
offset.x * offset.x +
offset.y * offset.y +
offset.z * offset.z)
if (this.options.logOcclusionDist) {
console.log(dist)
}
if (dist < this.options.occlusionDist) {
return false
}
}
return true
}
}

1 Volume controller for multiple players?

Is there an easy way to repeat this code to create a volume controller that works for 4 different players simultaneously? I've managed to get it to work for one player at any given point by changing the myAudio(x) value but any more than that seems to confuse it.
// Global variables and controls
var vid = document.getElementById("myVideo");
var volumeControl = document.getElementById("vol-control");
var setVolume = function(){
vid.volume = this.value / 100;
};
var audio = document.getElementById("myAudio(1)");
var volumeControl2 = document.getElementById("vol-control2");
var setVolume2 = function(){
audio.volume = this.value / 100;
};
//event listeners
volumeControl.addEventListener('change',setVolume);
volumeControl.addEventListener('input',setVolume);
volumeControl2.addEventListener('change',setVolume2);
volumeControl2.addEventListener('input',setVolume2);
Delegate
HTML
<div id="audvid">
<video.../>
<audeo.../>
</div>
event listeners
document.getElementById("audvid").addEventListener("change", setVolume)
document.getElementById("audvid").addEventListener("input", setVolume)
change whatever is accessed
const setVolume = function(e) {
const tgt = e.target;
if (["AUDIO", "VIDEO"].includes(tgt.tagName)) {
tgt.volume = tgt.value / 100;
}
};
change any audio/video
const setVolume = function(e) {
const tgt = e.target;
[...document.querySelectorAll("AUDIO", "VIDEO")].forEach(ele => {
ele.volume = tgt.value / 100;
})
};

How do you render a line markup with the Markups Extension?

I previously asked question about if there was a programatic way to render markups with the markups extension. This worked, or at least, for text markups! Now I am trying to do the same thing with line markups; however, I am stuck with one issue. How do you add the locations to the markup? I have an array of locations that I am trying to assign to it, but there doesn't seem to be a function, and when I try to directly assign locations with markup.location = [etc], for some reason it changes all the numbers to infinity.
So, how can I assign the location array to the markup?
This is how I am loading them:
let MarkupsCore = Autodesk.Viewing.Extensions.Markups.Core;
let line = new MarkupsCore.MarkupFreehand(25, markupTool); //eslint-disable-line
line.locations = [{x: 2, y: 3}]; //something like this
markupTool.addMarkup(line);
line.setSize({ x: markup.x, y: markup.y}, markup.width, markup.height);
line.updateStyle(true);
The MarkupFreehand cannot be used directly, it should be replaced by EditModeFreehand. It's also not easy to archive your request, to create a line markup, in a few codes. Here is the code snippet I used to create a line markup with the MarkupCore extension:
function createLineStartPt( mousePosX, mousePosY, editor ) {
const editMode = markup.editMode;
editor.snapper && editor.snapper.clearSnapped();
editMode.lastX = editMode.initialX = mousePosX;
editMode.lastY = editMode.initialY = mousePosY;
//set the starting point
const position = editMode.position = editor.clientToMarkups( mousePosX, mousePosY );
editMode.movements = [position];
const size = editMode.size = editor.sizeFromClientToMarkups( 1, 1 );
// Create pen.
editor.beginActionGroup();
const markupId = editor.getId();
const createPen = editMode.createPen( markupId, position, size, 0, [{x: 0, y: 0 }] );
createPen.execute();
editMode.createAbsolutePath( position );
editMode.selectedMarkup = editor.getMarkup( markupId );
editMode.creationBegin();
}
function createLineEndPt( mousePosX, mousePosY, editor ) {
const editMode = markup.editMode;
const selectedMarkup = editMode.selectedMarkup;
if( !selectedMarkup || !editMode.creating )
return;
const movements = editMode.movements;
const location = editor.clientToMarkups( mousePosX, mousePosY );
const dx = editMode.lastX - mousePosX;
const dy = editMode.lastY - mousePosY;
const moveTol = 25; // 5^2, compare to square to avoid using square root of distance
if( movements.length > 1 && (dx*dx + dy*dy) < moveTol ) {
movements[movements.length - 1] = location;
editMode.removeFromAbsolutePath( 1 );
} else {
movements.push( location );
editMode.lastX = mousePosX;
editMode.lastY = mousePosY;
}
editMode.addToAbsolutePath([location]);
const appendPen = editMode.setPen( editMode.position, editMode.size, editMode.absolutePath, true );
appendPen.execute();
}
function endLineDrawing( editor ) {
const editMode = markup.editMode;
if( !editMode.creating )
return editMode.creationEnd();
let movements = editMode.movements;
const cameraWidth = editMode.viewer.impl.camera.right - editMode.viewer.impl.camera.left;
const cameraHeight = editMode.viewer.impl.camera.top - editMode.viewer.impl.camera.bottom;
const cameraDiagSq = cameraWidth * cameraWidth + cameraHeight * cameraHeight;
movements = Autodesk.Viewing.Extensions.Markups.Core.Utils.simplify( movements, cameraDiagSq * 0.00000001, true );
const xs = movements.map(function(item) { return item.x });
const ys = movements.map(function(item) { return item.y });
const l = Math.min.apply(null, xs);
const t = Math.min.apply(null, ys);
const r = Math.max.apply(null, xs);
const b = Math.max.apply(null, ys);
const width = r - l; // Already in markup coords space
const height = b - t; // Already in markup coords space
const position = {
x: l + width * 0.5,
y: t + height * 0.5
};
const size = editMode.size = {x: width, y: height};
// Adjust points to relate from the shape's center
const locations = movements.map(function(point){
return {
x: point.x - position.x,
y: point.y - position.y
};
});
const endPen = editMode.setPen( position, size, locations, false );
endPen.execute();
editMode.creationEnd();
}
Then call them in this way:
// Load the extionsion
let markup;
viewer.loadExtension( 'Autodesk.Viewing.MarkupsCore' )
.then(function( markupsExt ) {
markup = markupsExt;
});
// Enter markup editer mode and change it to freehand mode
markup.enterEditMode();
const freehand = new Autodesk.Viewing.Extensions.Markups.Core.EditModeFreehand( markup );
markup.changeEditMode( freehand );
// Create start point of the line
createLineStartPt( 360.03125, 191.3125, markup );
// Create end point of the line
createLineEndPt( 460.03125, 191.3125, markup );
// Tell the markup tool to finish drawing
endLineDrawing( markup );
Here is the result of the above codes:
Note. All mousePos prefixed variables are coordinates in the client coordinate system in the browser viewport, see below link for details. After getting the mosue's clientX or clientY, you have to minus markup.svg.getBoundingClientRect() to adjust their values.
https://developer.mozilla.org/en-US/docs/Web/CSS/CSSOM_View/Coordinate_systems#Client
Hope it helps!

How Does This Background Image Work?

So This website:
http://www.atomicdust.com/
They have a background image on every page but when zooming, it doesn't change - ever. Not to mention how fast it loads. How is that possible to have content zoom without the background image? I could understand if it was a repeated image but this is not.
On that site it looks as though it's done by a script, as #rownage observes, but I've done it successfully in modern browsers using the (CSS3) "cover" property, i.e. background-size: cover; According to my notes (hurrah for comments!) this A List Apart article is where I got my information from.
On my (very much still-in-progress!) photo site, it looks like this at the moment. The background stays the same size when zooming in the browsers I've tried. Though it wouldn't surprise me if Internet Explorer couldn't cope with it.
As to how fast the images on that site load, the trick is simply a good choice of compressible image, well-compressed. The background that loaded when I visited their site is 1024x680 pixels, but because it's mostly black and white with large areas of plain colour, it compresses down to a pretty tiny 74KB.
It uses jQuery and this:
$.fn.bgResize = function(options) {
var defaults = {
imageWidth: 800,
imageHeight: 600
};
var options = $.extend(defaults, options);
var obj = $(this);
var initHtml = obj.html();
var windowHeight = $(window).height();
var windowWidth = $(window).width();
obj.height(windowHeight);
obj.width(windowWidth);
$('#work-wrapper').height(windowHeight);
$('#work-wrapper').width(windowWidth);
obj.html('<div id="inner-bg">'+initHtml+'</div>');
$('#inner-bg img').each(function(){
$(this).css({'display' : 'block', 'width' : '100%', 'height' : '100%'})
});
function doResize(){
var screenheight = $(window).height();
var screenwidth = $(window).width();
var imageheight = options.imageheight;
var imagewidth = options.imagewidth;
var ratio = imagewidth/imageheight;
var testwidth = screenheight * ratio;
var testheight = screenwidth / ratio;
obj.height(screenheight);
obj.width(screenwidth);
$('#work-wrapper').height(screenheight);
$('#work-wrapper').width(screenwidth);
if (testheight < screenheight){
obj.children('#inner-bg').width(testwidth);
obj.children('#inner-bg').height(testwidth/ratio);
var finalheight = Math.round(testwidth/ratio);
var finalwidth = testwidth;
var topoffset = (finalheight - screenheight)/2;
var leftoffset = (finalwidth - screenwidth)/2;
}
else if (testheight > screenheight){
obj.children('#inner-bg').height(testheight);
obj.children('#inner-bg').width(testheight * ratio);
var finalwidth = Math.round(testheight * ratio);
var finalheight = testheight;
var topoffset = (finalheight - screenheight)/2;
var leftoffset = (finalwidth - screenwidth)/2;
}
else {}
obj.children('#inner-bg').css("top", -topoffset);
obj.children('#inner-bg').css("left", -leftoffset);
}
doResize();
$(window).resize(function(){
doResize();
});
return this;
};