How to do PerspectiveCamera panning with Left mouse button in Trackball controls (r87)? - three.js

By default, Camera panning is with right mouse button
Control.panSpeed = 1.5;
but I want it to work in left mouse button. How to set this?

This requires an easy modification to the trackball code.
If you look at TrackballControls.js, the very beginning details the keycode-correlated state machine (source code):
THREE.TrackballControls = function ( object, domElement ) {
var _this = this;
var STATE = {NONE: -1, ROTATE: 0, ZOOM: 1, PN: 2, TOUCH_ROTATE: 3, TOUCH_ZOOM_PANE: 4};
this.object = object;
this.domElement = ( domElement !== undefined ) ? domElement : document;
// API
this.enabled = true;
this.screen = { left: 0, top: 0, width: 0, height: 0 };
Which is related to the keydown() function further down (source code):
function keydown( event ) {
if ( _this.enabled === false ) return;
window.removeEventListener( 'keydown', keydown );
_prevState = _state;
if ( _state !== STATE.NONE ) {
return;
} else if ( event.keyCode === _this.keys[ STATE.ROTATE ] && ! _this.noRotate ){
_state = STATE.ROTATE;
} else if ( event.keyCode === _this.keys[ STATE.ROTATE ] && ! _this.noZoom ){
_state = STATE.ZOOM;
} else if ( event.keyCode === _this.keys[ STATE.ROTATE ] && ! _this.noPan ){
_state = STATE.PAN;
}
So, I haven't had a chance to test this, but you should be able to change PAN in line 11 from 2 (the keycode for Right Click) to 0 (Left Click) or any other keycode you want. Remember to change ROTATE as well to avoid the bug-prone directive of rotating and panning at the same time.
EDIT: per #TheJim01's helpful correction, the actual click event triggering actually happens in function mousedown( event ) on line 393 of the script. event.button corresponds to the respective number in the state machine. Sorry about the confusion.

Related

In three.j,s what exactly does Object3D's traverseVisible() method do?

Does it:
Merely select objects within the camera's view frustum
Or does it actually do a z-depth type visibility check and only select those that are fully/partly visible on the screen - for a particular view
You can see what it does in the source code by clicking on the link at the bottom of the Object3D documentation.
traverseVisible( callback ) {
if ( this.visible === false ) return;
callback( this );
const children = this.children;
for ( let i = 0, l = children.length; i < l; i ++ ) {
children[ i ].traverseVisible( callback );
}
}
It only performs a check to see if visible === false, nothing more. There's no frustum or occlusion checks.

how to avoid multiple p5.js sketches to run all at once

I have a web page which encloses a few sketches, all written in P5.JS
Each sketch uses its own name space, so that it runs independently from the others.
I noticed that, for each sketch, the level of performance is lower than the one I get when it runs alone in a separate web page.
My question : what can I do to prevent all the sketches to run all at once ? Is it possible, for example, to activate a sketch only when the mouse hovers its canvas ? It would probably spare ressources.
Thank you for your help.
You can call noLoop() and loop() to stop and restart a sketch. There aren't any built in p5.js events to help you trigger noLoop() when the mouse leaves the sketch or when the sketch is scrolled off screen, however there are a couple of ways you can do it which rely on using the underlying browser functionality:
The built in mouseenter and mouseleave events
Checking winMouseX and winMouseY against the sketch canvas getBoundingClientRect() in each call to draw()
function makeSketch(...colorArgs) {
return (p) => {
let bgColor;
let black;
let c;
p.setup = () => {
c = p.createCanvas(p.windowWidth, p.windowHeight / 3);
bgColor = p.color(...colorArgs);
black = p.color(0);
c.elt.addEventListener('mouseenter', () => {
p.loop();
});
c.elt.addEventListener('mouseleave', () => {
p.noLoop();
});
let bounds = c.elt.getBoundingClientRect();
// Just in case the mouse is already over the canvas when it is created.
// This is also how you would use getBoundingClientRect from the draw()
// and mouseMoved() functions instead of the mouseenter/mouseleave events.
if (p.winMouseX < bounds.left ||
p.winMouseX > bounds.right ||
p.minMouseY < bounds.top ||
p.winMouseY > bounds.bottom) {
p.noLoop();
}
};
p.draw = () => {
p.background(p.lerpColor(
bgColor,
black,
p.abs((p.frameCount % 240 - 120) / 120)
));
let bounds = c.elt.getBoundingClientRect();
p.fill('white');
p.noStroke();
p.text(`${p.winMouseX}, ${p.winMouseY} :: ${bounds.left}, ${bounds.top}, ${bounds.right}, ${bounds.bottom}`, 10, 10);
}
};
}
let sketch1 = new p5(makeSketch('red'));
let sketch2 = new p5(makeSketch(0, 255, 0));
let sketch3 = new p5(makeSketch('blue'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.js"></script>
You might also find that it is sufficient to pause sketches that are off screen:
function makeSketch(...colorArgs) {
return (p) => {
let bgColor;
let black;
let c;
let isLooping;
p.setup = () => {
c = p.createCanvas(p.windowWidth, p.windowHeight);
bgColor = p.color(...colorArgs);
black = p.color(0);
let bounds = c.elt.getBoundingClientRect();
isLooping = true;
if (bounds.bottom < 0 ||
bounds.top > p.windowHeight) {
p.noLoop();
isLooping = false;
}
// Might need to check this on resize as well.
document.addEventListener('scroll', () => {
let bounds = c.elt.getBoundingClientRect();
// Note this only checks verticle scrolling, but you could check horizontal as well
if (bounds.bottom > 0 &&
bounds.top <= p.windowHeight) {
if (!isLooping) {
isLooping = true;
console.log(`sketch ${colorArgs.join(',')}: loop`);
p.loop();
}
} else if (isLooping) {
isLooping = false;
console.log(`sketch ${colorArgs.join(',')}: noLoop`);
p.noLoop();
}
});
};
p.draw = () => {
p.background(p.lerpColor(
bgColor,
black,
p.abs((p.frameCount % 240 - 120) / 120)
));
p.fill('white');
p.noStroke();
p.text(`${p.frameCount}`, 10, 10);
}
};
}
let sketch1 = new p5(makeSketch('red'));
let sketch2 = new p5(makeSketch(0, 255, 0));
let sketch3 = new p5(makeSketch('blue'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.js"></script>

ckeditor balloonpanel not staying attached to an element when scrolling

UPDATE balloon panels are staying attached in below code. We still have an issue where when we close a balloon panel, and then scroll afterward, the balloon panel that was just closed reappears. Here’s the updated code.
HERE WAS THE ORIGINAL QUESTION I am trying to get the ckeditor balloonpanel to stay attached to the element it was initially attached to; currently, when I scroll in the editor, the balloonpanels do not stay in place. The problem is that the balloonpanels shift when the user scrolls in the editor -- they do not remain attached to their initial element they were attached to when I scroll in the editor. Here is the code for the ckeditor plugin. It creates a balloonpanel in a for loop on return of an web service Ajax call and stores the panel in a global array called panels :
( function() {
var arr = new Array();
var panels = [];
var saveCmd = {
readOnly: 1,
modes: { wysiwyg: 1,source: 1 },
exec: function( editor ) {
if ( editor.fire( 'grade_level_score' ) ) {
var $form = editor.element.$.form;
/**
* Destroys the balloon panel by removing it from DOM and purging
* all associated event listeners.
*/
// https://github.com/ckeditor/ckeditor-dev/blob/64749bb245d1e91f6a4ac4e97c9648ec47acda91/plugins/balloonpanel/plugin.js#L743-L745
var panel;
while ( ( panel = panels.pop() ) ) {
panel.destroy();
}
arr = []; // clear the array of user-editable areas
panels = []; // clear the array of panels
// https://stackoverflow.com/a/48022658
var ele = $(editor.editable().$);
var elementOfClass;
var i = 1;
// class "ice-ice-editable" is in a span
$('span',ele).each(function(){
// https://stackoverflow.com/a/35866999
var iceIceEditableClass = "ice-ice-editable";
var hasClassIceIceEditable = $(this).hasClass(iceIceEditableClass);
if( hasClassIceIceEditable ) {
console.log($(this).text());
console.log($(this).attr('class'));
console.log($(this).attr('id'));
var userEditable = "user-editable-" + i;
// If the specified attribute already exists, only the value is set/changed.
this.setAttribute("id","user-editable-" + i);
var record1 = { id : userEditable , userEditableArea : $(this).text() };
arr.push(record1);
i++;
}
});
var gradeLevelObject = new Object();
gradeLevelObject.textAreas = arr;
// var responseGradeLevelScoreWS = gradeLevelScore(gradeLevelObject);
// BEGIN for testing
var result = '{"textAreas":[{"id":"user-editable-1","userEditableArea":"[Insert information specific to what is being addressed (a brief description of request(s) and/or concern(s). Specific training resource document for letter writing assistance will be referenced here.] ","score":22.24,"readingGrade":7,"issues":["asdf","zxcv"]},{"id":"user-editable-2","userEditableArea":"[Insert information specific to what is being addressed (a brief description of request(s) and/or concern(s). Specific training resource document for letter writing assistance will be referenced here.] ","score":22.24,"readingGrade":0,"issues":[]},{"id":"user-editable-3","userEditableArea":"[Insert information specific to what is being addressed (a brief description of request(s) and/or concern(s). Specific training resource document for letter writing assistance will be referenced here.] ","score":22.24,"readingGrade":0,"issues":[]},{"id":"user-editable-4","userEditableArea":"[Insert information specific to what is being addressed (a brief description of request(s) and/or concern(s). Specific training resource document for letter writing assistance will be referenced here.] ","score":22.24,"readingGrade":0,"issues":[]}]}';
var responseGradeLevelScoreWS = JSON.parse(result);
// END for testing
console.log(responseGradeLevelScoreWS);
var i;
for (i = 0; i < responseGradeLevelScoreWS.textAreas.length; i++){
if ( responseGradeLevelScoreWS.textAreas[i].readingGrade > 6 ) {
var j;
var issues = '';
for (j = 0; j < responseGradeLevelScoreWS.textAreas[i].issues.length; j++) {
issues += '<p>' + responseGradeLevelScoreWS.textAreas[i].issues[j] + '</p>';
}
panel = new CKEDITOR.ui.balloonPanel( editor, {
title: 'Grade: ' + responseGradeLevelScoreWS.textAreas[i].readingGrade + '. Score: ' + responseGradeLevelScoreWS.textAreas[i].score,
content: ( (typeof issues === 'undefined' || issues == null) ? 'There are no suggestions in order to descrease the grade level score' : issues ),
width: 500,
height: 120
});
var element = editor.document.getById(responseGradeLevelScoreWS.textAreas[i].id);
panel.attach( element );
panel.registerFocusable(element);
panels.push( panel );
issues = '';
}
}
// We'll use throttling for scroll listener to reduce performance impact.
var scrollListener = CKEDITOR.tools.eventsBuffer( 100, function() {
for (i = 0; i < panels.length; i++) {
panels[i].attach( editor.document.getById( responseGradeLevelScoreWS.textAreas[i].id ), {
focusElement: false,
show: false
} );
}
} );
editor.window.on( 'scroll', scrollListener.input );
if ( $form ) {
try {
//$form.submit();
} catch ( e ) {
// If there's a button named "submit" then the form.submit
// function is masked and can't be called in IE/FF, so we
// call the click() method of that button.
if ( $form.submit.click )
$form.submit.click();
}
}
}
}
};
var pluginName = 'grade_level_score';
// Register a plugin named "save".
CKEDITOR.plugins.add( pluginName, {
// jscs:disable maximumLineLength
lang: 'en,en-au,en-ca,en-gb,es,es-mx', // %REMOVE_LINE_CORE%
// jscs:enable maximumLineLength
icons: 'grade_level_score', // %REMOVE_LINE_CORE%
hidpi: true, // %REMOVE_LINE_CORE%
init: function( editor ) {
// Save plugin is for replace mode only.
if ( editor.elementMode != CKEDITOR.ELEMENT_MODE_REPLACE )
return;
var command = editor.addCommand( pluginName, saveCmd );
command.startDisabled = !( editor.element.$.form );
editor.ui.addButton && editor.ui.addButton( 'Grade_Level_Score', {
//label: editor.lang.save.toolbar,
label: "Grade Level Score",
command: pluginName,
toolbar: 'custom,100'
} );
}
} );
} )();
Only Balloon Toolbar has built-in functionality for automatic reposition on scroll. Balloon Panel itself is a static element. However, it can be easily achieved by attaching scroll listener and repositioning visible panels on scroll:
// We'll use throttling for scroll listener to reduce performance impact.
var scrollListener = CKEDITOR.tools.eventsBuffer( 100, function() {
for (i = 0; i < panels.length; i++) {
panels[i].attach( editor.document.getById(ids[i]), {
focusElement: false,
show: false
} );
}
} );
editor.window.on( 'scroll', scrollListener.input );
See this codepen for the full code (reusing some parts of your original code).

Bug found in scrolling smooth code found in someone else post but don't know how to fix

I search on my friend Google for some code to do smooth scroll and found this : Smooth vertical scrolling on mouse wheel in vanilla javascript?
It works well but if i scroll once and then try to use my mouse to manually move the scrollbar, it's broken...
SmoothScroll(document, 120, 12);
function SmoothScroll(target, speed, smooth) {
if (target === document)
target = (document.scrollingElement ||
document.documentElement ||
document.body.parentNode ||
document.body) // cross browser support for document scrolling
var moving = false
var pos = target.scrollTop
var frame = target === document.body &&
document.documentElement ?
document.documentElement :
target // safari is the new IE
target.addEventListener('scroll', scrolled, {
passive: false
})
target.addEventListener('mousewheel', scrolled, {
passive: false
})
target.addEventListener('DOMMouseScroll', scrolled, {
passive: false
})
function scrolled(e) {
e.preventDefault(); // disable default scrolling
var delta = normalizeWheelDelta(e)
pos += -delta * speed
pos = Math.max(0, Math.min(pos, target.scrollHeight - frame.clientHeight)) // limit scrolling
if (!moving) update()
}
function normalizeWheelDelta(e) {
if (e.detail) {
if (e.wheelDelta)
return e.wheelDelta / e.detail / 40 * (e.detail > 0 ? 1 : -1) // Opera
else
return -e.detail / 3 // Firefox
} else
return e.wheelDelta / 120 // IE,Safari,Chrome
}
function update() {
moving = true
var delta = (pos - target.scrollTop) / smooth
target.scrollTop += delta
if (Math.abs(delta) > 0.5)
requestFrame(update)
else
moving = false
}
var requestFrame = function () { // requestAnimationFrame cross browser
return (
window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function (func) {
window.setTimeout(func, 1000 / 50);
}
);
}()
}
So... i want it to work properly when i already scroll once but try to use the mouse to move the scrollbar instead of mousewheel.
Thanks for helping!
Looks like you could fix it by re-adjusting the pos variable to the scrollTop before your scrolling calculations.
Additionally theres a bug where your scroll could get stuck in an infinite render loop causing you to never stop animating. This was due to the delta being .5 < delta < 1 making the request frame get called forever. You cant actually move the scrollTop anything less than 1 so I adjusted the conditions for another render loop and rounded the delta
function scrolled(e) {
// if currently not animating make sure our pos is up to date with the current scroll postion
if(!moving) {
pos = target.scrollTop;
}
e.preventDefault(); // disable default scrolling
var delta = normalizeWheelDelta(e)
pos += -delta * speed
pos = Math.max(0, Math.min(pos, target.scrollHeight - frame.clientHeight)) // limit scrolling
if (!moving) update()
}
function update() {
moving = true;
// scrollTop is an integer and moving it by anything less than a whole number wont do anything
// to prevent a noop and an infinite loop we need to round it
var delta = absRound((pos - target.scrollTop) / smooth)
target.scrollTop += delta
if (Math.abs(delta) >= 1) {
requestFrame(update)
} else {
moving = false
}
}
function absRound(num) {
if(num < 0) {
return -1*Math.round(-1*num);
} else {
return Math.round(num);
}
}
That way when manually adjusting the scroll position if the wheel is used it doesnt jump to the position it was once at, but instead adjust itself to the current scroll position.

Texture Dispose Not Working in Three JS release 73

texture.dispose() doesn't seem to work in r73 but works fine in r75. What should I do to get it up and running on r73.
It doesn't either work in r77.
When using texture.dispose() the dispose normally should call onTextureDispose :
function onTextureDispose( event ) {
var texture = event.target;
texture.removeEventListener( 'dispose', onTextureDispose );
deallocateTexture( texture );
_infoMemory.textures --;
}
but it does not. Dont know if its a bug or something but in order to deallocate the texture I had to do a material.map.dispose() while I was clearing the scene (so to prepare for other texture to be inserted in the scene).
For example:
var clearScene = function(){
stopAnimate();
if(scene && scene.children.length>0){
for( var i = scene.children.length - 1; i >= 0; i-- ) {
if( scene.children[i] instanceof THREE.Sprite ){
if( scene.children[i].material.map ) scene.children[i].material.map.dispose();
}
if( scene.children[i] instanceof THREE.Mesh ){
if( scene.children[i].material.map ) scene.children[i].material.map.dispose();
}
scene.children[i].material.dispose();
scene.remove(scene.children[i]);
scene.children.splice(i,1);
}
}
};
Note: by using just:
scene.children[i].material.dispose();
the Textures of a material don't get disposed according to the documentation. http://threejs.org/docs/#Reference/Materials/Material
Hope that helps you.

Resources