there is an array of objects in the json file, the objects have current information about *.obj files and their paths. when referring to json as [...jsondata] array and calling each object in the scene through for(i=0; i<jsondata.lenght; i++) all are right, all objects are called up and the stage is lined up. But! The object.id assignment order is not the same as in the jsondata array, and for some reason always starts with id: 96. And most importantly id is not editable, and for me it is very important that the order in the scene was the same as in json. Can someone explain by what criteria threejs assigns this id?
Don't rely on the Object.id property to maintain any desired order. These are automatically generated, and they can add up quickly if any of your *.obj files have children, grandchildren, lights, cameras, etc.
For example, if you want 2 cars with id = 1 & 2, each part of the car could have a new id, depending on how it was built:
window: 2,
wheels: 3,
headlamps: 4,
light: 5
By the time you start car # 2, you're already on id = 6.
If you want to store a specific attribute in certain objects, you could use the .userData property to store it.
for(let i = 0; i < jsondata.length; i++) {
object[i].userData.order = i;
}
Or just create your own array to access them in order later:
const orderedItems = [];
for(let i = 0; i < jsondata.length; i++) {
orderedItems.push(someObject);
}
Related
I am thoroughly enjoying AMChart's many features but I couldn't find any way to dynamically add some children to a treemap.
I am trying to load additional children on "hit" for each element
for (var i = 0; i < this.maxDepthLevel; i++) {
const series = this.chart.seriesTemplates.create(i);
series.columns.template.events.on("hit", async function(ev) {
const data = ev.target.dataItem.dataContext;
children = await api.getChildrenOf(data.id);
ev.target.dataItem.treeMapDataItem.children.values.push(...children);
});
}
^ this doesn't work and when doing this and then zooming out, I get
I even tried changing the underlying data and then calling
this.chart.invalidateRawData();
but to no avail.
Does anyone have any experience with adding such dynamic children to a tree map?
I cannot simply load everything upfront, there are far too many possible layers of depth unfortunately and the request will be too large!
Rather than directly pushing child items to children values you need to do it like this :
for(var index=0; index < children.length; index++){
var newChildDataItem = new am4charts.TreeMapDataItem();
newChildDataItem.value = children[index].value;
newChildDataItem.name = children[index].name;
newChildDataItem.color = ev.target.dataItem.dataContext.color;
ev.target.dataItem.dataContext.children.insert(newChildDataItem);
}
Starting point (boundary conditions):
There is a set of recurring events each week (weekly events section
row 9:11).
My colleagues can set a starting date A6 and how many weeks they'd like to add A5.
Start situation:
Goal:
Running the script should add A5 number of weeks starting from the date A6 to the "upcoming events" section (row 22 and following) with the correct date.
This is how it would look like after the script ran successfully:
What works so far:
The script is able to add the recurring events for one week in the right order to the upcoming events section. It works as well if the starting date is in the middle of the week. (Not shown here as it is probably not relevant.)
My code:
function recurringWeeks() {
var ss = SpreadsheetApp.getActiveSheet(); // selects the active spreadsheet
var repeatingWeeks = ss.getRange(5,1).getValue(); // gets how many weeks it should repeat
var regWeek = ss.getRange(9, 2, 3, 7).getValues(); // gets the regular week data
var regWeekRepeated = ss.getRange(9, 2, repeatingWeeks*3, 7); // create an array to store the events for all weeks
// fill regWeekRepeated with regWeek
for (var j = 0; j < repeatingWeeks; j++){
for (var i = 0; i < 3; i++){
regWeekRepeated[i+j*3] = regWeek[i];
}
}
// Repeat week for "A5" times and add to start/end date a week
for (var j = 0; j < repeatingWeeks; j++){
for (var i = 0; i < 3; i++){
regWeekRepeated[i+j*3][0] = new Date(regWeek[i][0].getTime() + j*7*3600000*24); // <-This line leads to an error message.
}
}
ss.getRange(ss.getLastRow()+1,2,repeatingWeeks*3,7).setValues(regWeekRepeated); // copies weekly events after the last row
}
Edit of [i+j*6] to [i+j*3] in Repeat week for "A5" times and add to start/end date a week
Approach:
As I have solved how to add one week of recurring events with the correct date and right order, I use this as my "point of attack". I'm pretty sure that a for-loop does the job and this is currently my preferred tool.
create an array (regWeek) filled with the recurring event for one
week with the right order and dates. DONE
create an array (regWeekRepeated) and fill it with A5 number of
regular weeks (regWeek) starting from the date A6. ERROR 1:
Object does not allow properties to be added or changed.
make changes to the filled array regWeekRepeated. ERROR 2: TypeError: Cannot set property "0.0" of undefined to "(class)#3d8e4650"
Copy the values into the "upcoming events" section. DONE
Best hit I've found in the search results:
creating 2 dimension arrays
However, this uses .push and as far as I understand this means an element (can be a row) is placed at the end of an array. I've tried to use push as well but have not been successful yet.
Questions:
ERROR 1: Why is it not possible to assign the value of an element from array regWeek to array regWeekRepeated? Solved
ERROR 2: Is this property issue related to ERROR 1 or something different? I've tried to solve both errors individually. Solved
Which approach makes more sense (logically or performance wise) in this context: push individual rows at the end of an existing array or use the whole week as array building blocks?
A demo version of the spreadsheet
Update V01:
Changes: regWeekRepeated is now an array.
I've changed the for loop due to the feedback I've received.
// fill regWeekRepeated with regWeek
var regWeekRepeated = [];
for (var j = 0; j < repeatingWeeks; j++){
for (var i = 0; i < 3; i++){
regWeekRepeated.push(regWeek[i]);
}
}
Logger.log(regWeekRepeated)
Update V02:
// Repeat week for "A5" times and add to start/end date a week
for (var j = 0; j < repeatingWeeks; j++){
for (var i = 0; i < 3; i++){
regWeekRepeated[i+j*3][0] = new Date(regWeek[i][0].getTime() + j*7*3600000*24); //adds a week to the dates for each cycle
//Logger.log(regWeekRepeated[i]); // log is as expected and desired
}
Logger.log(regWeekRepeated); // second part of log not as expected.
}
//Logger.log(regWeekRepeated);
ss.getRange(ss.getLastRow()+1,2,repeatingWeeks*3,7).setValues(regWeekRepeated); // copies weekly events after the last row
Here the log output placed in the "outer" for loop.
1 represents the first cycle, 2 the second cycle
It looks like the second for loop overwrites the elements 0 to 2.
And here the output in google sheets
Update V03:
This makes sure that the changes don't affect the copy.
// fill regWeekRepeated with regWeek
for (var j = 0; j < repeatingWeeks; j++){
for (var i = 0; i < 3; i++){
regWeekRepeated[i+j*3] = regWeek[i].slice(); // shallow copy of an array
}
}
regWeekRepeated is not a array. getRange() doesn't return a array.
Try changing from
var regWeekRepeated = ss.getRange(9, 2, repeatingWeeks*3, 7); // create an array to store the events for all weeks
To
var regWeekRepeated = ss.getRange(9, 2, repeatingWeeks*3, 7).getValues(); // create an array to store the events for all weeks
Creating a array without touching the spreadsheet will increase performance.
var regWeekRepeated =[];
Making scripts for spreadsheets could be tricked because the spreadsheet and JavaScript jargons/lexics use the same terms in different ways. Perhaps this is what is happening here.
ERROR 1: Why is it not possible to assign the value of an element from array regWeek to array regWeekRepeated?
regWeekRepeated is a Range object not a JavaScript Array
ERROR 2: Is this property issue related to ERROR 1 or something different? I've tried to solve both errors individually.
Yes it's related. See the previous answer.
Which approach makes more sense (logically or performance wise) in this context: push individual rows at the end of an existing array or use the whole week as array building blocks?
We could say that calling Google Apps Scripts classes and methods are "expensive" so we should try to minimize the number calls to these kind of elements. One way to do this is by passing the range values to a JavaScript Array , then make all the changes directly to it and we finish pass the resulting values to the corresponding range.
To pass the values of a range to a JavaScript 2D array, use range.getValues() and to pass the JavaScript 2D array values to a range use range.setValues().
I'm trying to create a drop down menu with contents based on a another cell in the same row. For example if A1 = 'yes' then the drop down in B2 gives you the options of 'yes' or 'no'. I can do this I have the list data set up and to code works. The problem is I need to do this 155 times in 4 different sheets. Is there a faster way to do this than right clicking and editing the data validation rules for each cell. Here's a link to the test sheet I'm working on :
https://docs.google.com/spreadsheets/d/1rd_Ig_wpof9R_L0IiA1aZ9syO7BWxb6jvBhPqG8Jmm4/edit?usp=sharing
You can set data validation rules with a script, as documented here. Here's a reference for starting with Apps scripts.
I wrote a function that does approximately what you described. It works with the range B3:B157 of the sheet '9th grade' in the current spreadsheet. For each of them, it sets the validation rule to be: a value in the same row, columns B and C of sheet 'List Data'. The line with
....... = listData.getRange(i+3, 2, 1, 2);
will need to be modified if the source range of validation is to be different. Here, the parameters are: starting row, starting column, number of rows, number of columns. So, 2 columns starting with the second, in row numbered i+3.
function setRules() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var grade = ss.getSheetByName('9th Grade');
var listData = ss.getSheetByName('List Data');
var range = grade.getRange('B3:B157');
var rules = range.getDataValidations();
for (var i = 0; i < rules.length; i++) {
var sourceRange = listData.getRange(i+3, 2, 1, 2);
rules[i][0] = SpreadsheetApp.newDataValidation().requireValueInRange(sourceRange).build();
}
range.setDataValidations(rules);
}
I land in this issue for a diferent reason: "Just mass DataValidation copy (or update) in one column". Thanks, to user3717023 that bring me a light.
I hope that helps someone this simplification.
function setRules() {
//select spreadsheet
var ss = SpreadsheetApp.getActiveSpreadsheet();
var leads = ss.getSheetByName('Leads');
//Select correct Datavalidation
var rangeNewDataValidation = leads.getRange('M2:M2');
var rule = rangeNewDataValidation.getDataValidations();
//Copy (or Update) Datavalidation in a specific (13 or 'M') column
var newRule = rule[0][0].copy();
Logger.log(leads.getMaxRows())
for( var i=3; i <= leads.getMaxRows(); i++){
var range = leads.getRange(i, 13);
range.setDataValidations([[newRule.build()]]);
}
}
http://nvd3.org/ghpages/scatter.html
I want to build a chart thats very similar to the above example. I'm still in the beginning phases and am having a little difficulty trying to input my own data, either as a CSV or inline in the code.
I'm pretty sure that the code below is responsible for generating fake data to populate the chart, however, I can't seem to find the correct way to input real data. I've read over the "data" reference portions of the documentation on the d3 site and tried implementing the code but have not been able to get it to work. I believe the problem is structuring. The question is, how do I find out how I need to structure the data array and use that, as input for the data?
Here is the live example of where I'm building my version of the chart.
http://goo.gl/XHela
Here is the code that is generating the random data:
function randomData(groups, points) { //# groups,# points per group
var data = [],
shapes = ['circle', 'cross', 'triangle-up', 'triangle-down', 'diamond', 'square'],
random = d3.random.normal();
for (i = 0; i < groups; i++) {
data.push({
key: 'Group ' + i,
values: []
});
for (j = 0; j < points; j++) {
data[i].values.push({
x: random(),
y: random(),
size: Math.random(),
shape: shapes[j % 6]
});
}
}
return data;
}
Before your response I tried to output the results of the function randomData to the page, using the following code, (the same just modified).
function see(groups, points) { //# groups,# points per group
var data = [],
shapes = ['circle', 'cross', 'triangle-up', 'triangle-down', 'diamond', 'square'],
random = d3.random.normal();
for (i = 0; i < groups; i++) {
data.push({
key: 'Group ' + i,
values: []
});
for (j = 0; j < points; j++) {
data[i].values.push({
x: random(),
y: random(),
size: Math.random(),
shape: shapes[j % 6]
});
}
}
data.toString();
var x=document.getElementById("demo");
x.innerHTML=data;
}
But on the page the output is
[object Object],[object Object],[object Object],[object Object]
I guess the follow up question is how do I use my own data for this chart and not generated data?
You can see how the data is structured by stepping through the code using Chrome Developer tools' console in Chrome. In the page you linked above (first link) Pressing Ctrl + Shift + C in Windows or Command + Shift + c in OSX will bring up this window. Click on Sources and find the file scatter.js from the list of files on the left. Set a break point at the line: return data. Referesh the page. Code execution will pause at that line. Now go the the Console and type data. The data used for the chart will be output. You can browse the structure and get an idea of how it was created.
You should see something like this in the end:
I'm trying to build in a change history, rather than a diff style history, I've opted to save the entire object.
This becomes problematic because each copy of the object updates along side the original object.
The Kinetic.Node.clone method seemed like the right thing for me, but it doesn't seem to do what I expect it to do.
Pseudocode:
var History = function(){
var h = this;
h.history = [];
h.pointer = -1;
h.save = function() {
h.history = h.history.slice(0,h.pointer);
h.history.push(im.Stage.createCopy());
h.movePointer(1);
};
h.movePointer = function(diff) {
h.pointer += diff;
(h.pointer < 0 && (h.pointer = 0));
(h.pointer >= h.history.length && (h.pointer = h.history.length-1));
return h.pointer;
};
h.render = function() {
im.Stage = h.history[h.pointer].createCopy();
im.Stage.draw();
};
h.undo = function() {
h.movePointer(-1);
h.render();
};
h.redo = function() {
h.movePointer(1);
h.render();
};
};
How can I create an accurate copy of the stage?
The best method to build up a history system is to store your layers/elements after each operation into an array as serialized values, using layer.toJSON() .
If you use images on layers and eventhandlers that you want to display/work after you restore anything from the history then you will have to reattache images and eventhandlers to the objects/layers/etc because toJSON() does not store images and eventhandlers as it would have been too large data stored. Build up your history like this:
first, try to use projeqht's answer on this question .
second you will have to reattach the images and eventhandlers. With a trick given by markE on this question you can easily handle it.
The best thing for an accurate representation is to do:
var layers = stage.getChildren();
to get the layers. Then do:
var layerChildren = new Array();
for(var i=0; i<layers.length; i++)
layerChildren[i] = layers.getChildren();
each time you want to save a state.
This will store all your layers and their children in an array. Fairly efficient and accurate.
Now you just have to save the list of layers and list of children somewhere and then you can move back and forth between states.