Update texture of mesh using ThreeJS & Dat.Gui - reactjs

I would like the texture of my Mesh to update when clicked on function.
When you click on the 'UpdateMateria' function i'd like the mesh to dispose its current texture and add a new one.
Animation Loop
startAnimationLoop = () => {
const tableBoard = this.scene.getObjectByName('tableSurface');
tableBoard.material.map = this.updateMateria;
this.renderer.render(this.scene, this.camera);
this.requestID = window.requestAnimationFrame(this.startAnimationLoop);
};
Dat.Gui
userGUI = () => {
const update = {
updateMateria: function() {
alert('Changing');
this.material.dispose();
this.material.map = texture1();
}
}
this.gui = new dat.GUI();
const controls = function() {
this.title = new controls();
this.gui.add(update, 'updateMateria')
}
}
When i put the function straight into the 'Animation Loop' this actually updates the texture to the desired one, but the current version gives me 'TypeError: Cannot read property 'dispose' of undefined'

First, you can not use the this reference in updateMateria (probably updateMaterial???). Consider to safe the this reference in a variable outside of this function and use this variable instead. Besides, it's not necessary to dispose the material if you just want to change the texture.
userGUI = () => {
const scope = this;
const update = {
updateMateria: function() {
alert('Changing');
// scope.material.dispose();
scope.material.map = texture1();
}
}
this.gui = new dat.GUI();
const controls = function() {
this.title = new controls();
this.gui.add(update, 'updateMateria')
}
three.js R108

Related

How can I remove last shape layer when creating new one in lefalet

According to this codesandbox I'm using to to generate a map on react and I have implemented the drawer plugin. here I want to delete the last shape I have added to the map and create and show the new map instead of the last shape. Is there nay performant way of doing that?
Store the last layer in a variable in the create event:
var lastLayer = null;
map.on(L.Draw.Event.CREATED, function (e) {
var type = e.layerType,
layer = e.layer;
if (type === "marker") {
const { lat, lng } = layer._latlng;
console.log(lat, lng);
}
if (type === "rectangle") {
const latlngs = layer._latlngs;
let thisPpp = "";
latlngs[0].map((item) => {
return (thisPpp += `LatLng(${item.lat}, ${item.lng}),`);
});
}
lastLayer = layer; // <---------------
drawnItems.addLayer(layer);
});
And then you can remove the last layer with:
if(lastLayer){
lastLayer.remove();
}

How to group GLTF element and drag and drop in 3js

Single GLTF element i can drag and drop. but group of element i can't drag. I am using following code
var loader = new THREE.GLTFLoader();
loader.load( 'W3030/W3030.gltf', ( gltf ) => {
gltf.scene.traverse( function( child ) {
if(child.type === "Group")
{
newObject = true;
GLTFobjects.push(child);
}
if ( child.isMesh ) {
child.receiveShadow = true;
child.castShadow = true;
child.material.transparent = true;
child.material.opacity = 1;
}
});
scene.add(GLTFobjects);
gltf.scene.scale.set(1, 1, 1);
});
I'm afraid instances of Group are not supported by DragControls since there is no Group.raycast() method.
You can implement a workaround by replacing groups with invisible meshes. However, instead of setting Object3D.visible to false, you do this for Material.visible. Otherwise the raycasting logic will not perform the intersection test. It's then necessary to use a geometry that is large enough to enclose the respective children.
three.js R110
Hi Thanks for your support. Now i drag.
Use following code to drag multi mesh GLTF. It is Work for me.
var dragobjects =[];
//add following code in init function
var gltfobject = addGLTFObjectIntoScene();
scene.add(gltfobject);
dragControls = new THREE.DragControls(dragobjects, camera, renderer.domElement);
dragControls.addEventListener('dragstart', onDragStart, false);
dragControls.addEventListener('drag', onDrag , false);
dragControls.addEventListener('dragend', onDragEnd, false);
//end init function code
//add following function after or before init function
function drawBox(objectwidth,objectheight,objectdepth){
var geometry, material, box;
geometry = new THREE.BoxGeometry(objectwidth,objectheight,objectdepth);
material = new THREE.MeshBasicMaterial({color: 0xffff00, transparent: true, opacity: 0,depthTest:false});
box = new THREE.Mesh(geometry, material);
dragobjects.push(box);
box.position.set(0, 0, 0);
return box;
};
function addGLTFObjectIntoScene(){
group = new THREE.Group();
var loader = new THREE.GLTFLoader();
loader.load( 'W1230/W1230.gltf', ( gltf ) => {
mesh = gltf.scene;
mesh.scale.set( 30, 30, 30);
var gltfbox = new THREE.Box3().setFromObject( mesh );
var objectwidth = Math.floor(gltfbox.getSize().x);
var objectheight = Math.floor(gltfbox.getSize().y);
var objectdepth = Math.floor(gltfbox.getSize().z);
objectwidth = objectwidth + parseInt(2);
objectheight = objectheight + parseInt(2);
objectdepth = objectdepth + parseInt(1);
mesh.position.set(0, -objectheight/2, 0);
box = drawBox(objectwidth,objectheight,objectdepth);
group.add(box);
group.name = "quadrant";
console.log(mesh);
box.add( mesh);
});
return group;
};

Reactjs waiting for array.forEach completion before continued with callback

I've only been using JS and React for a short time, and am running into issues with waiting for a forEach loop to complete before continuing.
The function glitchLib below should pull an array of img sources from state, iterate through the elements of the array and "glitch" each image (the actual process of glitching is done with a javascript library). For each glitched image, I want to push a 2-elem array with the original source and glitched source into currentSaved[], and then pass the array of arrays in a callback.
glitchLib() {
const currentSaved = [];
var array = this.state.originalFiles;
array.forEach(function(src) {
var originalImage = src;
const image = new Image();
image.src = src;
image.onload = () => {
glitch()
.fromImage(image)
.toDataURL()
.then((dataURL) => {
const dataArray = [originalImage, dataURL];
currentSaved.push(dataArray);
});
};
});
this.props.callback(currentSaved);
}
If I wrap the callback in a setTimeout for ~10 seconds or so, the array is properly iterated through so there isn't any issue with the way the js library is performing the "glitching", which should just return a base64 image encoding. Without the setTimeout, an empty array is passed.
What is the proper way to wait for the array to be fully iterated through (or for that matter, is there any better way of doing this sort of thing)?
You can wait for the completion of a number of Promises using Promise.all():
const glitch = () => Promise.resolve('xyz')
function glitchLib(callback) {
const promises = []
const array = ['abc', 'def']
array.forEach(src => {
const originalImage = src
const image = new Image()
image.src = src
/*image.onload = */;(() => {
promises.push(
glitch()
//.fromImage(image)
//.toDataURL()
.then(dataURL => [originalImage, dataURL])
)
})()
})
Promise.all(promises)
.then(currentSaved => callback(currentSaved))
}
glitchLib(x => console.log(x))

amCharts not displaying when the page loads in AngularJs

I am using amCharts to display some data and calling this in my init function. The following is my code:
export default class ACtrl {
constructor($scope, $http, $state) {
var sampleCharts = function () {
let sampleChart;
let sampleBarGraph;
let sampleLine;
const writeAlarmsample = data => {
const {
total,
users
} = data;
// Set cumulative percentage
let runningTotal = 0;
users.forEach(user => {
runningTotal += user.assetAvailability;
user.cumulativePercentage = runningTotal / total * 100;
});
sampleChart.dataProvider = users;
sampleChart.write('userAvailability');
sampleChart.validateData();
};
function handleClick(event){
$state.go("app.userdetails", { userID: event.item.category });
}
// Alarm sample
AmCharts.ready(() => {
sampleChart = new AmCharts.AmSerialChart();
sampleChart.categoryField = "assetId";
//add click listener
sampleChart.addListener("clickGraphItem", handleClick);
var yAxis = new AmCharts.ValueAxis();
yAxis.position = "left";
sampleChart.addValueAxis(yAxis);
var yAxis2 = new AmCharts.ValueAxis();
yAxis2.position = "right";
sampleChart.addValueAxis(yAxis2);
sampleBarGraph = new AmCharts.AmGraph();
sampleBarGraph.valueField = "userAvailability";
sampleBarGraph.type = "column";
sampleBarGraph.fillAlphas = 1;
sampleBarGraph.lineColor = "#f0ab00";
sampleBarGraph.valueAxis = yAxis;
sampleChart.addGraph(sampleBarGraph);
sampleLine = new AmCharts.AmGraph();
sampleLine.valueField = "cumulativePercentage";
sampleLine.type = "line";
sampleLine.lineColor = "#cb0044";
sampleLine.valueAxis = yAxis2;
sampleChart.addGraph(sampleLine);
sampleChart.write('userAvailability');
$http.get(constants.LOCAL_HOST+"/dashboard/users")
.then(response => writeAlarmsample(response.data));
});
};
$scope.init = function() {
availabilityCharts();
};
})
}
The charts load fine when I hit the refresh button, but they are not loaded when the page gets loaded for the first time. I also have a refresh button which calls the function to load the charts even that does not load the charts. If I click on one of the chart items it takes me to details page and when I come back to this page the charts do not load. I have to click on refresh again to load the charts. Can anyone let me know what is the reason for this issue and how I can fix it.

AngularJS. Return new factory instance

I'm a newbie in AngularJS and have faced the issue.
Can I reinject my factory singleton object across all controllers, where it's been injected?
For example:
.factory('medicalCenterService', function(MedicalCenterResource) {
var medicalCenterService = {};
medicalCenterService.currentMedCenter = MedicalCenterResource.get();
medicalCenterService.reloadMedCenter = function() {
medicalCenterService.currentMedCenter = MedicalCenterResource.get();
return medicalCenterService.currentMedCenter;
};
medicalCenterService.updateMedicalCenter = function(medicalCenter) {
MedicalCenterResource.updateMedicalCenter(medicalCenter);
medicalCenterService.currentMedCenter = medicalCenter;
};
return medicalCenterService;
})
In MedicalCenterController I get singleton object with medical center when application starts:
function MedicalCenterController($scope, medicalCenterService) {
$scope.currentMedCenter = medicalCenterService.currentMedCenter;
}
But later I try to edit medical center fields (name, address, etc..) in AccountProfileController
function AccountProfileController($scope, medicalCenterService) {
$scope.currentMedCenter = medicalCenterService.currentMedCenter;
$scope.applyMedCenterChanges = function (currentMedCenter) {
medicalCenterService.updateMedicalCenter(currentMedCenter);
};
}
And what I'm expecting to have is the object with updated fields.
How to return a new instance of my singleton?
Do you want something like this?
.factory('MedicalCenter', function(MedicalCenterResource) {
var MedicalCenter = function () {
var center = MedicalCenterResource.get(),
update = function() {
MedicalCenterResource.updateMedicalCenter(center)
};
return {
center: center,
update: update
}
};
return MedicalCenter;
})
function MedicalCenterController($scope, MedicalCenter) {
center = new MedicalCenter();
$scope.currentMedCenter = center.center;
}
function AccountProfileController($scope, MedicalCenter) {
center = new MedicalCenter();
$scope.currentMedCenter = center.center;
$scope.applyMedCenterChanges = function () {
center.update();
};
}
Like you wrote in post services are Singletons and its good way to share data over services. However if you want to create new instance of factory/service, you can't do that but we can create list of objects in one service/factory where each list item represents different instance. Something like:
.factory('medicalCenterService', function(MedicalCenterResource) {
var medicalCenterServices = [
{ctrlName: 'MedicalCenterController',medicalCenterService: {/*....*/}},
{ctrlName: 'AccountProfileController',medicalCenterService: {/*....*/}},
];
//......
})

Resources