diff --git a/changes.md b/changes.md index d7d1dcb81..76a522073 100644 --- a/changes.md +++ b/changes.md @@ -2,6 +2,7 @@ ## Changes in dev +1. Support std::vector in `RNtuple` 1. Resort order of ranges in http request, fixing several long-standing problems #374 1. Implement for `TPie` 3d, text, title drawing including interactivity 1. Implement `TCanvas` support in `build3d` function #373 diff --git a/modules/draw/RNTuple.mjs b/modules/draw/RNTuple.mjs index c1c1ee977..f0c48f578 100644 --- a/modules/draw/RNTuple.mjs +++ b/modules/draw/RNTuple.mjs @@ -10,13 +10,15 @@ async function drawRNTuple(dom, obj, opt) { const args = {}; let tuple; - if (obj?.$tuple) { + if (obj?.$tuple && obj.$field) { // case of fictional ROOT::RNTupleField tuple = obj.$tuple; args.expr = obj._name; - if (isStr(opt) && opt.indexOf('dump') === 0) + if (isStr(opt) && opt.indexOf('dump') === 0) { args.expr += '>>' + opt; - else if (opt) + args.branch = obj.$field; + args.copy_fields = false; // no need to copy fields, reading is simple + } else if (opt) args.expr += opt; } else { tuple = obj; diff --git a/modules/rntuple.mjs b/modules/rntuple.mjs index a6568f517..3e0638d8f 100644 --- a/modules/rntuple.mjs +++ b/modules/rntuple.mjs @@ -851,9 +851,20 @@ class RNTupleDescriptorBuilder { } } + /** @summary Return all childs of specified field */ + findChildFields(field) { + const indx = this.fieldDescriptors.indexOf(field), res = []; + for (let n = 0; n < this.fieldDescriptors.length; ++n) { + const fld = this.fieldDescriptors[n]; + if ((fld !== field) && (fld.parentFieldId === indx)) + res.push(fld); + } + return res; + } + /** @summary Return array of columns for specified field */ - findColumns(name) { - const res = [], field = this.findField(name); + findColumns(field) { + const res = []; if (!field) return res; for (const colDesc of this.columnDescriptors) { @@ -863,6 +874,7 @@ class RNTupleDescriptorBuilder { return res; } + } // class RNTupleDescriptorBuilder @@ -1105,6 +1117,7 @@ class ReaderItem { /** @summary identify if this item used as offset for std::string or similar */ is_offset_item() { return this.item1; } + /** @summary implements reading of std::string where item1 provides offsets */ assignStringReader(item1) { this.item1 = item1; this.off0 = 0; @@ -1130,7 +1143,7 @@ class ReaderItem { this.shift = function(entries) { if (entries > 0) { - this.item1.shift0(entries); + this.item1.shift0(entries - 1); this.item1.func0(this.$tgt); this.off0 = Number(this.$tgt[this.name]); this.shift_o(this.off0); @@ -1138,6 +1151,44 @@ class ReaderItem { }; } + /** @summary implement reading of std::vector where item1 provides elements numbers */ + assignVectorReader(item1) { + this.item1 = item1; + this.off0 = 0; + this.$tgt = {}; + + item1.func0 = item1.func; + item1.shift0 = item1.shift; + // assign noop + item1.func = item1.shift = () => {}; + + // remember own read function - they need to be used + this.func0 = this.func; + this.shift0 = this.shift; + + this.func = function(tgtobj) { + this.item1.func0(this.$tgt); + const off = Number(this.$tgt[this.name]); + let len = off - this.off0; + const arr = [], tmp = {}; + while (len-- > 0) { + this.func0(tmp); + arr.push(tmp[this.name]); + } + tgtobj[this.name] = arr; + this.off0 = off; + }; + + this.shift = function(entries) { + if (entries > 0) { + this.item1.shift0(entries - 1); + this.item1.func0(this.$tgt); + this.off0 = Number(this.$tgt[this.name]); + this.shift0(this.off0); + } + }; + } + collectPages(cluster_locations, dataToRead, itemsToRead, pagesToRead, emin, emax, elist) { const pages = cluster_locations[this.id].pages; @@ -1304,29 +1355,47 @@ async function rntupleProcess(rntuple, selector, args = {}) { }); } + function addFieldReading(field, tgtname) { + const columns = rntuple.builder.findColumns(field); + if (!columns?.length) + throw new Error(`No columns found for field '${field.fieldName}' in RNTuple`); + + let item = new ReaderItem(columns[0], tgtname); + item.assignReadFunc(); + handle.arr.push(item); + + if ((columns.length === 2) && (field.typeName === 'std::string')) { + const item2 = new ReaderItem(columns[1], tgtname); + item2.assignStringReader(item); + handle.arr.push(item2); + item = item2; // second item performs complete reading of the string + } + + const childs = rntuple.builder.findChildFields(field); + if ((childs.length === 1) && (field.typeName.indexOf('std::vector') === 0)) { + const item2 = addFieldReading(childs[0], tgtname); + item2.assignVectorReader(item); + item = item2; // second item makes actual reading of vector + } + + return item; + } + return readHeaderFooter(rntuple).then(res => { if (!res) throw new Error('Not able to read header for the RNtuple'); for (let i = 0; i < selector.numBranches(); ++i) { - const name = getSelectorFieldName(selector, i); + const name = getSelectorFieldName(selector, i), + tgtname = selector.nameOfBranch(i); if (!name) throw new Error(`Not able to extract name for field ${i}`); - const columns = rntuple.builder.findColumns(name); - if (!columns?.length) - throw new Error(`No columns found for field '${name}' in RNTuple`); + const field = rntuple.builder.findField(name); + if (!field) + throw new Error(`Field ${name} not found`); - const tgtname = selector.nameOfBranch(i), - item = new ReaderItem(columns[0], tgtname); - item.assignReadFunc(); - handle.arr.push(item); - - if (columns.length === 2) { - const item2 = new ReaderItem(columns[1], tgtname); - item2.assignStringReader(item); - handle.arr.push(item2); - } + addFieldReading(field, tgtname); } // calculate number of entries diff --git a/modules/tree.mjs b/modules/tree.mjs index ff2abc99f..882938151 100644 --- a/modules/tree.mjs +++ b/modules/tree.mjs @@ -1128,7 +1128,7 @@ class TDrawSelector extends TSelector { this.leaf = args.leaf; // branch object remains, therefore we need to copy fields to see them all - this.copy_fields = ((args.branch.fLeaves?.arr.length > 1) || args.branch.fBranches?.arr.length) && !args.leaf; + this.copy_fields = args.copy_fields ?? (((args.branch.fLeaves?.arr.length > 1) || args.branch.fBranches?.arr.length) && !args.leaf); this.addBranch(branch, 'br0', args.direct_branch); // add branch