From babc8b427bb96bec86d9fc433e38172ba55d6f7d Mon Sep 17 00:00:00 2001 From: Jonas Rembser Date: Fri, 8 Mar 2024 16:05:12 +0100 Subject: [PATCH 01/12] [RF] Don't support `proof` anymore in RooFit and RooStats The PROOF interface in RooFit/RooStats didn't work anymore for a long time, so nobody will be suprised if this is removed. --- roofit/roofitcore/CMakeLists.txt | 2 - roofit/roofitcore/inc/LinkDef.h | 1 - .../roofitcore/inc/RooProofDriverSelector.h | 46 ------ roofit/roofitcore/inc/RooStudyManager.h | 4 - .../roofitcore/src/RooProofDriverSelector.cxx | 95 ------------- roofit/roofitcore/src/RooStudyManager.cxx | 67 --------- roofit/roostats/CMakeLists.txt | 3 - roofit/roostats/inc/LinkDef.h | 2 - .../roostats/inc/RooStats/HypoTestInverter.h | 5 - roofit/roostats/inc/RooStats/ProofConfig.h | 117 --------------- roofit/roostats/inc/RooStats/ToyMCSampler.h | 6 - roofit/roostats/inc/RooStats/ToyMCStudy.h | 96 ------------- roofit/roostats/src/HypoTestInverter.cxx | 13 -- roofit/roostats/src/ToyMCSampler.cxx | 54 +------ roofit/roostats/src/ToyMCStudy.cxx | 134 ------------------ tutorials/roostats/HybridInstructional.C | 21 --- tutorials/roostats/HybridStandardForm.C | 14 -- tutorials/roostats/IntervalExamples.C | 7 - .../OneSidedFrequentistUpperLimitWithBands.C | 14 -- .../roostats/StandardFeldmanCousinsDemo.C | 5 - .../roostats/StandardFrequentistDiscovery.C | 5 - tutorials/roostats/StandardHypoTestDemo.C | 8 -- tutorials/roostats/StandardHypoTestInvDemo.C | 21 +-- .../StandardTestStatDistributionDemo.C | 8 -- .../TwoSidedFrequentistUpperLimitWithBands.C | 14 -- 25 files changed, 3 insertions(+), 759 deletions(-) delete mode 100644 roofit/roofitcore/inc/RooProofDriverSelector.h delete mode 100644 roofit/roofitcore/src/RooProofDriverSelector.cxx delete mode 100644 roofit/roostats/inc/RooStats/ProofConfig.h delete mode 100644 roofit/roostats/inc/RooStats/ToyMCStudy.h delete mode 100644 roofit/roostats/src/ToyMCStudy.cxx diff --git a/roofit/roofitcore/CMakeLists.txt b/roofit/roofitcore/CMakeLists.txt index d82756854233f..f4833d6c55212 100644 --- a/roofit/roofitcore/CMakeLists.txt +++ b/roofit/roofitcore/CMakeLists.txt @@ -191,7 +191,6 @@ ROOT_STANDARD_LIBRARY_PACKAGE(RooFitCore RooProduct.h RooProfileLL.h RooProjectedPdf.h - RooProofDriverSelector.h RooPullVar.h RooQuasiRandomGenerator.h RooRandom.h @@ -391,7 +390,6 @@ ROOT_STANDARD_LIBRARY_PACKAGE(RooFitCore src/RooProduct.cxx src/RooProfileLL.cxx src/RooProjectedPdf.cxx - src/RooProofDriverSelector.cxx src/RooPullVar.cxx src/RooQuasiRandomGenerator.cxx src/RooRandom.cxx diff --git a/roofit/roofitcore/inc/LinkDef.h b/roofit/roofitcore/inc/LinkDef.h index 83fa0c52e8ac5..fe0b0b5c687ae 100644 --- a/roofit/roofitcore/inc/LinkDef.h +++ b/roofit/roofitcore/inc/LinkDef.h @@ -325,7 +325,6 @@ #pragma link C++ class RooStudyPackage+ ; #pragma link C++ class RooAbsStudy+ ; #pragma link C++ class RooGenFitStudy+ ; -#pragma link C++ class RooProofDriverSelector+ ; #pragma link C++ class RooExtendedBinding+ ; #pragma link C++ class std::list+ ; #pragma link C++ class std::map+ ; diff --git a/roofit/roofitcore/inc/RooProofDriverSelector.h b/roofit/roofitcore/inc/RooProofDriverSelector.h deleted file mode 100644 index e7c686cb409ff..0000000000000 --- a/roofit/roofitcore/inc/RooProofDriverSelector.h +++ /dev/null @@ -1,46 +0,0 @@ -////////////////////////////////////////////////////////// -// This class has been automatically generated on -// Fri Jul 10 11:01:34 2009 by ROOT version 5.23/05 -// from TTree t/t -// found on file: Memory Directory -////////////////////////////////////////////////////////// - -#ifndef RooProofDriverSelector_h -#define RooProofDriverSelector_h - -#include -#include - -class RooStudyPackage ; - -class RooProofDriverSelector : public TSelector { -public : - TTree *fChain = nullptr; ///GetTree()->GetEntry(entry, getall) : 0; } - void SetOption(const char *option) override { fOption = option; } - void SetObject(TObject *obj) override { fObject = obj; } - void SetInputList(TList *input) override { fInput = input; } - void SlaveTerminate() override ; - TList *GetOutputList() const override { return fOutput; } - - RooStudyPackage* _pkg = nullptr; - Int_t seed ; - - ClassDefOverride(RooProofDriverSelector,0); -}; - -#endif - diff --git a/roofit/roofitcore/inc/RooStudyManager.h b/roofit/roofitcore/inc/RooStudyManager.h index 466e16d73ac69..b554dbc50d767 100644 --- a/roofit/roofitcore/inc/RooStudyManager.h +++ b/roofit/roofitcore/inc/RooStudyManager.h @@ -41,10 +41,6 @@ class RooStudyManager : public TNamed { // Interactive running void run(Int_t nExperiments) ; - // PROOF-based parallel running - void runProof(Int_t nExperiments, const char* proofHost="", bool showGui=true) ; - static void closeProof(Option_t *option = "s") ; - // Batch running void prepareBatchInput(const char* studyName, Int_t nExpPerJob, bool unifiedInput) ; void processBatchOutput(const char* filePat) ; diff --git a/roofit/roofitcore/src/RooProofDriverSelector.cxx b/roofit/roofitcore/src/RooProofDriverSelector.cxx deleted file mode 100644 index 84b18dc4ce17f..0000000000000 --- a/roofit/roofitcore/src/RooProofDriverSelector.cxx +++ /dev/null @@ -1,95 +0,0 @@ -#define RooProofDriverSelector_cxx -// The class definition in RooProofDriverSelector.h has been generated automatically -// by the ROOT utility TTree::MakeSelector(). This class is derived -// from the ROOT class TSelector. For more information on the TSelector -// framework see $ROOTSYS/README/README.SELECTOR or the ROOT User Manual. - -// The following methods are defined in this file: -// Begin(): called every time a loop on the tree starts, -// a convenient place to create your histograms. -// SlaveBegin(): called after Begin(), when on PROOF called only on the -// slave servers. -// Process(): called for each event, in this function you decide what -// to read and fill your histograms. -// SlaveTerminate: called at the end of the loop on the tree, when on PROOF -// called only on the slave servers. -// Terminate(): called at the end of the loop on the tree, -// a convenient place to draw/fit your histograms. -// -// To use this file, try the following session on your Tree T: -// -// Root > T->Process("RooProofDriverSelector.C") -// Root > T->Process("RooProofDriverSelector.C","some options") -// Root > T->Process("RooProofDriverSelector.C+") -// - -#include "RooProofDriverSelector.h" -#include "RooDataSet.h" -#include "RooWorkspace.h" -#include "RooAbsPdf.h" -#include "RooRealVar.h" -#include "RooFitResult.h" -#include "TRandom.h" -#include "RooRandom.h" -#include "RooAbsStudy.h" -#include "RooStudyPackage.h" -#include "RooGlobalFunc.h" - -using namespace RooFit; -using std::cout, std::endl; - -void RooProofDriverSelector::SlaveBegin(TTree * /*tree*/) -{ - // Retrieve study pack - _pkg=nullptr ; - if (fInput) { - for (auto * tmp : dynamic_range_cast(*fInput)) { - if (tmp) { - _pkg = tmp ; - } - } - } - if (_pkg==nullptr) { - cout << "RooProofDriverSelector::SlaveBegin() no RooStudyPackage found, aborting process" << endl ; - fStatus = kAbortProcess ; - } else { - cout << "workspace contents = " << endl ; - _pkg->wspace().Print() ; - - // Initialize study pack - seed = _pkg->initRandom() ; - _pkg->initialize() ; - } - -} - -bool RooProofDriverSelector::Process(Long64_t entry) -{ - cout << "RooProofDriverSelector::Process(" << entry << ")" << endl ; - _pkg->runOne() ; - return true; -} - - -void RooProofDriverSelector::SlaveTerminate() -{ - _pkg->finalize() ; - _pkg->exportData(fOutput,seed) ; -} - - - -void RooProofDriverSelector::Init(TTree *tree) -{ - // Set branch addresses and branch pointers - if (!tree) return; - fChain = tree; - fChain->SetMakeClass(1); - fChain->SetBranchAddress("i", &i, &b_i); -} - -bool RooProofDriverSelector::Notify() -{ - return true; -} - diff --git a/roofit/roofitcore/src/RooStudyManager.cxx b/roofit/roofitcore/src/RooStudyManager.cxx index 0bebddff7af8d..14362dbc585ce 100644 --- a/roofit/roofitcore/src/RooStudyManager.cxx +++ b/roofit/roofitcore/src/RooStudyManager.cxx @@ -89,73 +89,6 @@ void RooStudyManager::run(Int_t nExperiments) } - -//////////////////////////////////////////////////////////////////////////////// -/// Open PROOF-Lite session - -void RooStudyManager::runProof(Int_t nExperiments, const char* proofHost, bool showGui) -{ - coutP(Generation) << "RooStudyManager::runProof(" << GetName() << ") opening PROOF session" << endl ; - void* p = reinterpret_cast(gROOT->ProcessLineFast(Form("TProof::Open(\"%s\")",proofHost))); - - // Check that PROOF initialization actually succeeded - if (p==nullptr) { - coutE(Generation) << "RooStudyManager::runProof(" << GetName() << ") ERROR initializing proof, aborting" << endl ; - return ; - } - - // Suppress GUI if so requested - if (!showGui) { - gROOT->ProcessLineFast(Form("((TProof*)0x%zx)->SetProgressDialog(0) ;",reinterpret_cast(p))) ; - } - - // Propagate workspace to proof nodes - coutP(Generation) << "RooStudyManager::runProof(" << GetName() << ") sending work package to PROOF servers" << endl ; - gROOT->ProcessLineFast(Form("((TProof*)0x%zx)->AddInput((TObject*)0x%zx) ;",reinterpret_cast(p),reinterpret_cast(_pkg)) ) ; - - // Run selector in parallel - coutP(Generation) << "RooStudyManager::runProof(" << GetName() << ") starting PROOF processing of " << nExperiments << " experiments" << endl ; - - gROOT->ProcessLineFast(Form("((TProof*)0x%zx)->Process(\"RooProofDriverSelector\",%d) ;",reinterpret_cast(p),nExperiments)) ; - - // Aggregate results data - coutP(Generation) << "RooStudyManager::runProof(" << GetName() << ") aggregating results data" << endl ; - TList* olist = reinterpret_cast(gROOT->ProcessLineFast(Form("((TProof*)0x%zx)->GetOutputList()",reinterpret_cast(p)))); - aggregateData(olist) ; - - // cleaning up - coutP(Generation) << "RooStudyManager::runProof(" << GetName() << ") cleaning up input list" << endl ; - gROOT->ProcessLineFast(Form("((TProof*)0x%zx)->GetInputList()->Remove((TObject*)0x%zx) ;",reinterpret_cast(p),reinterpret_cast(_pkg)) ) ; - -} - - -//////////////////////////////////////////////////////////////////////////////// -/// "Option_t *option" takes the parameters forwarded to gProof->Close(option). -/// -/// This function is intended for scripts that run in loops -/// where it is essential to properly close all connections and delete -/// the TProof instance (frees ports). - -void RooStudyManager::closeProof(Option_t *option) -{ - if (gROOT->GetListOfProofs()->LastIndex() != -1 && gROOT->ProcessLineFast("gProof;")) - { - gROOT->ProcessLineFast(Form("gProof->Close(\"%s\") ;",option)) ; - gROOT->ProcessLineFast("gProof->CloseProgressDialog() ;") ; - - // CloseProgressDialog does not do anything when run without GUI. This detects - // whether the proof instance is still there and deletes it if that is the case. - if (gROOT->GetListOfProofs()->LastIndex() != -1 && gROOT->ProcessLineFast("gProof;")) { - gROOT->ProcessLineFast("delete gProof ;") ; - } - } else { - ooccoutI(nullptr,Generation) << "RooStudyManager: No global Proof objects. No connections closed." << endl ; - } -} - - - //////////////////////////////////////////////////////////////////////////////// void RooStudyManager::prepareBatchInput(const char* studyName, Int_t nExpPerJob, bool unifiedInput=false) diff --git a/roofit/roostats/CMakeLists.txt b/roofit/roostats/CMakeLists.txt index 692161f9112e3..1acf9604fc704 100644 --- a/roofit/roostats/CMakeLists.txt +++ b/roofit/roostats/CMakeLists.txt @@ -59,7 +59,6 @@ ROOT_STANDARD_LIBRARY_PACKAGE(RooStats RooStats/ProfileInspector.h RooStats/ProfileLikelihoodCalculator.h RooStats/ProfileLikelihoodTestStat.h - RooStats/ProofConfig.h RooStats/ProposalFunction.h RooStats/ProposalHelper.h RooStats/RatioOfProfiledLikelihoodsTestStat.h @@ -74,7 +73,6 @@ ROOT_STANDARD_LIBRARY_PACKAGE(RooStats RooStats/TestStatSampler.h RooStats/ToyMCImportanceSampler.h RooStats/ToyMCSampler.h - RooStats/ToyMCStudy.h RooStats/UniformProposal.h RooStats/UpperLimitMCSModule.h SOURCES @@ -122,7 +120,6 @@ ROOT_STANDARD_LIBRARY_PACKAGE(RooStats src/SPlot.cxx src/ToyMCImportanceSampler.cxx src/ToyMCSampler.cxx - src/ToyMCStudy.cxx src/UniformProposal.cxx src/UpperLimitMCSModule.cxx DICTIONARY_OPTIONS diff --git a/roofit/roostats/inc/LinkDef.h b/roofit/roostats/inc/LinkDef.h index 9dc000f37eafd..22e3b40087202 100644 --- a/roofit/roostats/inc/LinkDef.h +++ b/roofit/roostats/inc/LinkDef.h @@ -63,8 +63,6 @@ #pragma link C++ class RooStats::TestStatSampler+; // interface, not concrete #pragma link C++ class RooStats::DebuggingSampler+; #pragma link C++ class RooStats::ToyMCSampler+; -#pragma link C++ class RooStats::ToyMCStudy+; -#pragma link C++ class RooStats::ProofConfig+; #pragma link C++ class RooStats::ToyMCImportanceSampler+; #pragma link C++ class RooStats::ToyMCPayload+; diff --git a/roofit/roostats/inc/RooStats/HypoTestInverter.h b/roofit/roostats/inc/RooStats/HypoTestInverter.h index b909d0009e4f0..0168c86c0c968 100644 --- a/roofit/roostats/inc/RooStats/HypoTestInverter.h +++ b/roofit/roostats/inc/RooStats/HypoTestInverter.h @@ -146,10 +146,6 @@ class HypoTestInverter : public IntervalCalculator { /// set numerical error in test statistic evaluation (default is zero) void SetNumErr(double err) { fNumErr = err; } - /// set flag to close proof for every new run - static void SetCloseProof(bool flag); - - protected: /// copy c-tor @@ -174,7 +170,6 @@ class HypoTestInverter : public IntervalCalculator { static double fgCLAccuracy; static double fgAbsAccuracy; static double fgRelAccuracy; - static bool fgCloseProof; static std::string fgAlgo; // graph, used to compute the limit, not just for plotting! diff --git a/roofit/roostats/inc/RooStats/ProofConfig.h b/roofit/roostats/inc/RooStats/ProofConfig.h deleted file mode 100644 index 3117fdaba7a02..0000000000000 --- a/roofit/roostats/inc/RooStats/ProofConfig.h +++ /dev/null @@ -1,117 +0,0 @@ -// @(#)root/roostats:$Id$ -// Author: Kyle Cranmer and Sven Kreiss July 2010 -/************************************************************************* - * Copyright (C) 1995-2008, Rene Brun and Fons Rademakers. * - * All rights reserved. * - * * - * For the licensing terms see $ROOTSYS/LICENSE. * - * For the list of contributors see $ROOTSYS/README/CREDITS. * - *************************************************************************/ - -#ifndef ROOSTATS_ProofConfig -#define ROOSTATS_ProofConfig - - - - -#include "Rtypes.h" - -#include "RooWorkspace.h" -#include "RooStudyManager.h" - -#include "TROOT.h" - -namespace RooStats { - -/** \class ProofConfig - \ingroup Roostats - -Holds configuration options for proof and proof-lite. - -This class will be expanded in the future to hold more specific configuration -options for the tools in RooStats. - -Access to TProof::Mgr for configuration is still possible as usual -(e.g. to set Root Version to be used on workers). You can do: - -~~~ {.cpp} - TProof::Mgr("my.server.url")->ShowROOTVersions() - TProof::Mgr("my.server.url")->SetROOTVersion("v5-27-06_dbg") -~~~ - -*/ - - -class ProofConfig { - - public: - - /// configure proof with number of experiments and host session - /// in case of Prooflite, it is better to define the number of workers as "worker=n" in the host string - ProofConfig(RooWorkspace &w, Int_t nExperiments = 0, const char *host = "", bool showGui = false) : - fWorkspace(w), - fNExperiments(nExperiments), - fHost(host), - fShowGui(showGui) - { - - // case of ProofLite - if (fHost == "" || fHost.Contains("lite") ) { - fLite = true; - - - // get the default value of the machine - use CINT interface until we have a poper PROOF interface that we can call - int nMaxWorkers = gROOT->ProcessLineFast("TProofLite::GetNumberOfWorkers()"); - - if (nExperiments == 0) { - fNExperiments = nMaxWorkers; - } - - if (nExperiments > nMaxWorkers) { - std::cout << "ProofConfig - Warning: using a number of workers = " << nExperiments - << " which is larger than the number of cores in the machine " << nMaxWorkers << std::endl; - } - - // set the number of workers in the Host string - fHost = TString::Format("workers=%d",fNExperiments); - } - else { - fLite = false; - // have always a default number of experiments - if (nExperiments == 0) fNExperiments = 8; - } - } - - - virtual ~ProofConfig() { - ProofConfig::CloseProof(); - } - - /// close all proof connections - static void CloseProof(Option_t *option = "s") { RooStudyManager::closeProof(option); } - - /// returns fWorkspace - RooWorkspace& GetWorkspace(void) const { return fWorkspace; } - /// returns fHost - const char* GetHost(void) const { return fHost; } - /// return fNExperiments - Int_t GetNExperiments(void) const { return fNExperiments; } - /// return fShowGui - bool GetShowGui(void) const { return fShowGui; } - /// return true if it is a Lite session (ProofLite) - bool IsLite() const { return fLite; } - - protected: - RooWorkspace& fWorkspace; ///< workspace that is to be used with the RooStudyManager - Int_t fNExperiments; ///< number of experiments. This is sometimes called "events" in proof; "experiments" in RooStudyManager. - TString fHost; ///< Proof hostname. Use empty string (ie "") for proof-lite. Can also handle options like "workers=2" to run on two nodes. - bool fShowGui; ///< Whether to show the Proof Progress window. - bool fLite; ///< Whether we have a Proof Lite session - - protected: - ClassDef(ProofConfig,1) // Configuration options for proof. -}; -} - - -#endif diff --git a/roofit/roostats/inc/RooStats/ToyMCSampler.h b/roofit/roostats/inc/RooStats/ToyMCSampler.h index 26fd6a4358a08..40afb15bc3c10 100644 --- a/roofit/roostats/inc/RooStats/ToyMCSampler.h +++ b/roofit/roostats/inc/RooStats/ToyMCSampler.h @@ -17,7 +17,6 @@ #include "RooStats/SamplingDistribution.h" #include "RooStats/TestStatistic.h" #include "RooStats/ModelConfig.h" -#include "RooStats/ProofConfig.h" #include "RooWorkspace.h" #include "RooMsgService.h" @@ -226,9 +225,6 @@ class ToyMCSampler: public TestStatSampler { fAdaptiveLowLimit = low_threshold; } - /// calling with argument or nullptr deactivates proof - void SetProofConfig(ProofConfig *pc = nullptr) { fProofConfig = pc; } - void SetProtoData(const RooDataSet* d) { fProtoData = d; } protected: @@ -274,8 +270,6 @@ class ToyMCSampler: public TestStatSampler { const RooDataSet *fProtoData = nullptr; ///< in dev - ProofConfig *fProofConfig = nullptr; /// #include #include @@ -81,8 +79,6 @@ double HypoTestInverter::fgAbsAccuracy = 0.05; double HypoTestInverter::fgRelAccuracy = 0.05; std::string HypoTestInverter::fgAlgo = "logSecant"; -bool HypoTestInverter::fgCloseProof = false; - // helper class to wrap the functionality of the various HypoTestCalculators template @@ -92,13 +88,6 @@ struct HypoTestWrapper { }; -//////////////////////////////////////////////////////////////////////////////// -/// set flag to close proof for every new run - -void HypoTestInverter::SetCloseProof(bool flag) { - fgCloseProof = flag; -} - //////////////////////////////////////////////////////////////////////////////// /// get the variable to scan /// try first with null model if not go to alternate model @@ -512,8 +501,6 @@ HypoTestInverterResult* HypoTestInverter::GetInterval() const { oocoutE(nullptr,Eval) << "HypoTestInverter::GetInterval - error running an auto scan " << std::endl; } - if (fgCloseProof) ProofConfig::CloseProof(); - return static_cast (fResults->Clone()); } diff --git a/roofit/roostats/src/ToyMCSampler.cxx b/roofit/roostats/src/ToyMCSampler.cxx index bb2684b07a689..75f01a51f6fb9 100644 --- a/roofit/roostats/src/ToyMCSampler.cxx +++ b/roofit/roostats/src/ToyMCSampler.cxx @@ -23,10 +23,6 @@ at each call to nextPoint(...). ToyMCSampler is an implementation of the TestStatSampler interface. It generates Toy Monte Carlo for a given parameter point and evaluates a TestStatistic. - -For parallel runs, ToyMCSampler can be given an instance of ProofConfig -and then run in parallel using proof or proof-lite. Internally, it uses -ToyMCStudy with the RooStudyManager. */ #include "RooStats/ToyMCSampler.h" @@ -42,7 +38,6 @@ ToyMCStudy with the RooStudyManager. #include "RooRandom.h" #include "RooStudyManager.h" -#include "RooStats/ToyMCStudy.h" #include "RooStats/DetailedOutputAggregator.h" #include "RooStats/RooStatsUtils.h" #include "RooSimultaneous.h" @@ -265,57 +260,12 @@ SamplingDistribution* ToyMCSampler::GetSamplingDistribution(RooArgSet& paramPoin RooDataSet* ToyMCSampler::GetSamplingDistributions(RooArgSet& paramPointIn) { - // ======= S I N G L E R U N ? ======= - if(!fProofConfig) - return GetSamplingDistributionsSingleWorker(paramPointIn); - - // ======= P A R A L L E L R U N ======= - if (!CheckConfig()){ - oocoutE(nullptr, InputArguments) - << "Bad COnfiguration in ToyMCSampler " - << endl; - return nullptr; - } - - // turn adaptive sampling off if given - if(fToysInTails) { - fToysInTails = 0; - oocoutW(nullptr, InputArguments) - << "Adaptive sampling in ToyMCSampler is not supported for parallel runs." - << endl; - } - - // adjust number of toys on the slaves to keep the total number of toys constant - Int_t totToys = fNToys; - fNToys = (int)ceil((double)fNToys / (double)fProofConfig->GetNExperiments()); // round up - - // create the study instance for parallel processing - ToyMCStudy toymcstudy{}; - toymcstudy.SetToyMCSampler(*this); - toymcstudy.SetParamPoint(paramPointIn); - toymcstudy.SetRandomSeed(RooRandom::randomGenerator()->Integer(TMath::Limits::Max() ) ); - - // temporary workspace for proof to avoid messing with TRef - RooWorkspace w(fProofConfig->GetWorkspace()); - RooStudyManager studymanager(w, toymcstudy); - studymanager.runProof(fProofConfig->GetNExperiments(), fProofConfig->GetHost(), fProofConfig->GetShowGui()); - - RooDataSet* output = toymcstudy.merge(); - - // reset the number of toys - fNToys = totToys; - - return output; + return GetSamplingDistributionsSingleWorker(paramPointIn); } //////////////////////////////////////////////////////////////////////////////// -/// This is the main function for serial runs. It is called automatically -/// from inside GetSamplingDistribution when no ProofConfig is given. -/// You should not call this function yourself. This function should -/// be used by ToyMCStudy on the workers (ie. when you explicitly want -/// a serial run although ProofConfig is present). -/// +/// This is the main function for serial runs. RooDataSet* ToyMCSampler::GetSamplingDistributionsSingleWorker(RooArgSet& paramPointIn) { diff --git a/roofit/roostats/src/ToyMCStudy.cxx b/roofit/roostats/src/ToyMCStudy.cxx deleted file mode 100644 index 82e9f9ce9d01b..0000000000000 --- a/roofit/roostats/src/ToyMCStudy.cxx +++ /dev/null @@ -1,134 +0,0 @@ -// @(#)root/roostats:$Id$ -// Author: Sven Kreiss and Kyle Cranmer June 2010 -/************************************************************************* - * Copyright (C) 1995-2008, Rene Brun and Fons Rademakers. * - * All rights reserved. * - * * - * For the licensing terms see $ROOTSYS/LICENSE. * - * For the list of contributors see $ROOTSYS/README/CREDITS. * - *************************************************************************/ - -/** \class RooStats::ToyMCStudy - \ingroup Roostats - -ToyMCStudy is an implementation of RooAbsStudy for toy Monte Carlo sampling. -This class is automatically used by ToyMCSampler when given a ProofConfig. -This is also its intended use case. -*/ - -#include "RooStats/ToyMCStudy.h" - -#include "RooStats/ToyMCSampler.h" - - -#include "RooMsgService.h" - -#include "RooRandom.h" -#include "TRandom2.h" -#include "TMath.h" - -#include "TEnv.h" - -ClassImp(RooStats::ToyMCStudy); - -ClassImp(RooStats::ToyMCPayload); - -using std::endl; - - -namespace RooStats { - -//////////////////////////////////////////////////////////////////////////////// - -bool ToyMCStudy::initialize(void) { - coutP(Generation) << "initialize" << endl; - - if(!fToyMCSampler) { - coutE(InputArguments) << "Need an instance of ToyMCSampler to run." << endl; - return false; - }else{ - coutI(InputArguments) << "Using given ToyMCSampler." << endl; - } - - - TString worknumber = gEnv->GetValue("ProofServ.Ordinal","undef"); - int iworker = -1; - if (worknumber != "undef") { - iworker = int( worknumber.Atof()*10 + 0.1); - - // generate a seed using - std::cout << "Current global seed is " << fRandomSeed << std::endl; - TRandom2 r(fRandomSeed ); - // get a seed using the iworker-value - unsigned int seed = r.Integer(TMath::Limits::Max() ); - for (int i = 0; i< iworker; ++i) - seed = r.Integer(TMath::Limits::Max() ); - - // initialize worker using seed from ToyMCSampler - RooRandom::randomGenerator()->SetSeed(seed); - } - - coutI(InputArguments) << "Worker " << iworker << " seed is: " << RooRandom::randomGenerator()->GetSeed() << endl; - - return false; -} - -//////////////////////////////////////////////////////////////////////////////// - -bool ToyMCStudy::execute(void) { - - coutP(Generation) << "ToyMCStudy::execute - run with seed " << RooRandom::randomGenerator()->Integer(TMath::Limits::Max() ) << std::endl; - RooDataSet* sd = fToyMCSampler->GetSamplingDistributionsSingleWorker(fParamPoint); - storeDetailedOutput(std::make_unique(sd)); - - return false; -} - -//////////////////////////////////////////////////////////////////////////////// - -bool ToyMCStudy::finalize(void) { - coutP(Generation) << "ToyMCStudy::finalize" << endl; - - if(fToyMCSampler) delete fToyMCSampler; - fToyMCSampler = nullptr; - - return false; -} - -//////////////////////////////////////////////////////////////////////////////// - -RooDataSet* ToyMCStudy::merge() { - - RooDataSet* samplingOutput = nullptr; - - if(!detailedData()) { - coutE(Generation) << "ToyMCStudy::merge No detailed output present." << endl; - return nullptr; - } - - int i = 0; - for (auto * o : static_range_cast(*detailedData())) { - ToyMCPayload *oneWorker = dynamic_cast< ToyMCPayload* >(o); - if(!oneWorker) { - coutW(Generation) << "Merging Results problem: not correct type" << endl; - continue; - } - - if (!samplingOutput) { - samplingOutput = new RooDataSet(*oneWorker->GetSamplingDistributions()); - - } else { - samplingOutput->append(*oneWorker->GetSamplingDistributions()); - } - - i++; - //delete oneWorker; - } - coutP(Generation) << "Merged data from nworkers # " << i << "- merged data size is " << samplingOutput->numEntries() << std::endl; - - - return samplingOutput; -} - - -} // end namespace RooStats diff --git a/tutorials/roostats/HybridInstructional.C b/tutorials/roostats/HybridInstructional.C index 5cece56f8f752..4d23c722b8eb9 100644 --- a/tutorials/roostats/HybridInstructional.C +++ b/tutorials/roostats/HybridInstructional.C @@ -155,14 +155,6 @@ void HybridInstructional(int ntoys = 6000) // of the current threshold on messages. RooFit::MsgLevel msglevel = RooMsgService::instance().globalKillBelow(); - // Use PROOF-lite on multi-core machines - ProofConfig *pc = NULL; - // uncomment below if you want to use PROOF - // ~~~ - // pc = new ProofConfig(*w, 4, "workers=4", kFALSE); // machine with 4 cores - // pc = new ProofConfig(*w, 2, "workers=2", kFALSE); // machine with 2 cores - // ~~~ - // ---------------------------------------------------- // P A R T 2 : D I R E C T I N T E G R A T I O N // ==================================================== @@ -333,11 +325,6 @@ void HybridInstructional(int ntoys = 6000) // hc1.ForcePriorNuisanceNull(*w->pdf("lognorm_prior")); // ~~~ - // enable proof - // NOTE: This test statistic is defined in this macro, and is not - // working with PROOF currently. Luckily test stat is fast to evaluate. - // `if(pc) toymcs1->SetProofConfig(pc);` - // these lines save current msg level and then kill any messages below ERROR RooMsgService::instance().setGlobalKillBelow(RooFit::ERROR); // Get the result @@ -386,10 +373,6 @@ void HybridInstructional(int ntoys = 6000) // hc2.ForcePriorNuisanceNull(*w->pdf("lognorm_prior")); // ~~~ - // enable proof - if (pc) - toymcs2->SetProofConfig(pc); - // these lines save current msg level and then kill any messages below ERROR RooMsgService::instance().setGlobalKillBelow(RooFit::ERROR); // Get the result @@ -495,10 +478,6 @@ void HybridInstructional(int ntoys = 6000) // toymcs3->SetTestStatistic(&ropl); // toymcs3->SetTestStatistic(&mlets); - // enable proof - if (pc) - toymcs3->SetProofConfig(pc); - // these lines save current msg level and then kill any messages below ERROR RooMsgService::instance().setGlobalKillBelow(RooFit::ERROR); // Get the result diff --git a/tutorials/roostats/HybridStandardForm.C b/tutorials/roostats/HybridStandardForm.C index 25b8f5d112d77..c6fd9aa002776 100644 --- a/tutorials/roostats/HybridStandardForm.C +++ b/tutorials/roostats/HybridStandardForm.C @@ -185,12 +185,6 @@ void HybridStandardForm(int ntoys = 6000) // of the current threshold on messages. RooFit::MsgLevel msglevel = RooMsgService::instance().globalKillBelow(); - // Use PROOF-lite on multi-core machines - ProofConfig *pc = NULL; - // uncomment below if you want to use PROOF - pc = new ProofConfig(*w, 4, "workers=4", kFALSE); // machine with 4 cores - // pc = new ProofConfig(*w, 2, "workers=2", kFALSE); // machine with 2 cores - //----------------------------------------------- // P A R T 3 : A N A L Y T I C R E S U L T // ============================================== @@ -325,10 +319,6 @@ void HybridStandardForm(int ntoys = 6000) // hc1.ForcePriorNuisanceNull(*w->pdf("lognorm_prior")); // ~~~ - // enable proof - // proof not enabled for this test statistic - // if(pc) toymcs1->SetProofConfig(pc); - // these lines save current msg level and then kill any messages below ERROR RooMsgService::instance().setGlobalKillBelow(RooFit::ERROR); // Get the result @@ -379,10 +369,6 @@ void HybridStandardForm(int ntoys = 6000) // hc2.ForcePriorNuisanceNull(*w->pdf("lognorm_prior")); // ~~~ - // enable proof - if (pc) - toymcs2->SetProofConfig(pc); - // these lines save current msg level and then kill any messages below ERROR RooMsgService::instance().setGlobalKillBelow(RooFit::ERROR); // Get the result diff --git a/tutorials/roostats/IntervalExamples.C b/tutorials/roostats/IntervalExamples.C index f07db2eb12db8..58085256e8a90 100644 --- a/tutorials/roostats/IntervalExamples.C +++ b/tutorials/roostats/IntervalExamples.C @@ -30,7 +30,6 @@ #include "RooStats/MCMCIntervalPlot.h" #include "RooStats/LikelihoodIntervalPlot.h" -#include "RooStats/ProofConfig.h" #include "RooStats/ToyMCSampler.h" #include "RooRandom.h" @@ -106,12 +105,6 @@ void IntervalExamples() // The PDF could be extended and this could be removed fc.FluctuateNumDataEntries(false); - // Proof - // ProofConfig pc(*wspace, 4, "workers=4", kFALSE); // proof-lite - // ProofConfig pc(w, 8, "localhost"); // proof cluster at "localhost" - // ToyMCSampler* toymcsampler = (ToyMCSampler*) fc.GetTestStatSampler(); - // toymcsampler->SetProofConfig(&pc); // enable proof - PointSetInterval *interval = (PointSetInterval *)fc.GetInterval(); // example use of BayesianCalculator diff --git a/tutorials/roostats/OneSidedFrequentistUpperLimitWithBands.C b/tutorials/roostats/OneSidedFrequentistUpperLimitWithBands.C index 167ac995d4354..e75af9260b803 100644 --- a/tutorials/roostats/OneSidedFrequentistUpperLimitWithBands.C +++ b/tutorials/roostats/OneSidedFrequentistUpperLimitWithBands.C @@ -126,9 +126,6 @@ using namespace RooFit; using namespace RooStats; -bool useProof = false; // flag to control whether to use Proof -int nworkers = 0; // number of workers (default use all available cores) - // ------------------------------------------------------- // The actual macro @@ -244,17 +241,6 @@ void OneSidedFrequentistUpperLimitWithBands(const char *infile = "", const char cout << "Not sure what to do about this model" << endl; } - // We can use PROOF to speed things along in parallel - // However, the test statistic has to be installed on the workers - // so either turn off PROOF or include the modified test statistic - // in your `$ROOTSYS/roofit/roostats/inc` directory, - // add the additional line to the LinkDef.h file, - // and recompile root. - if (useProof) { - ProofConfig pc(*w, nworkers, "", false); - toymcsampler->SetProofConfig(&pc); // enable proof - } - if (mc->GetGlobalObservables()) { cout << "will use global observables for unconditional ensemble" << endl; mc->GetGlobalObservables()->Print(); diff --git a/tutorials/roostats/StandardFeldmanCousinsDemo.C b/tutorials/roostats/StandardFeldmanCousinsDemo.C index debd8e19f2276..d0ebfb243aa98 100644 --- a/tutorials/roostats/StandardFeldmanCousinsDemo.C +++ b/tutorials/roostats/StandardFeldmanCousinsDemo.C @@ -130,11 +130,6 @@ void StandardFeldmanCousinsDemo(const char *infile = "", const char *workspaceNa cout << "Not sure what to do about this model" << endl; } - // We can use PROOF to speed things along in parallel - // ProofConfig pc(*w, 1, "workers=4", kFALSE); - // ToyMCSampler* toymcsampler = (ToyMCSampler*) fc.GetTestStatSampler(); - // toymcsampler->SetProofConfig(&pc); // enable proof - // Now get the interval PointSetInterval *interval = fc.GetInterval(); ConfidenceBelt *belt = fc.GetConfidenceBelt(); diff --git a/tutorials/roostats/StandardFrequentistDiscovery.C b/tutorials/roostats/StandardFrequentistDiscovery.C index 7a8fc23eb8aef..b110357a0cb63 100644 --- a/tutorials/roostats/StandardFrequentistDiscovery.C +++ b/tutorials/roostats/StandardFrequentistDiscovery.C @@ -155,11 +155,6 @@ double StandardFrequentistDiscovery(const char *infile = "", const char *workspa cout << "Not sure what to do about this model" << endl; } - // We can use PROOF to speed things along in parallel - // ProofConfig pc(*w, 2, "user@yourfavoriteproofcluster", false); - ProofConfig pc(*w, 2, "", false); - // toymcs.SetProofConfig(&pc); // enable proof - // instantiate the calculator FrequentistCalculator freqCalc(*data, *mc, *mcNull, &toymcs); freqCalc.SetToys(toys, toys); // null toys, alt toys diff --git a/tutorials/roostats/StandardHypoTestDemo.C b/tutorials/roostats/StandardHypoTestDemo.C index 09c9bf404e024..f1badc6638fa7 100644 --- a/tutorials/roostats/StandardHypoTestDemo.C +++ b/tutorials/roostats/StandardHypoTestDemo.C @@ -70,7 +70,6 @@ struct HypoTestOptions { double poiValue = -1; // change poi snapshot value for S+B model (needed for expected p0 values) int printLevel = 0; bool generateBinned = false; // for binned generation - bool useProof = false; // use Proof bool enableDetailedOutput = false; // for detailed output }; @@ -88,7 +87,6 @@ void StandardHypoTestDemo(const char *infile = "", const char *workspaceName = " double poiValue = optHT.poiValue; // change poi snapshot value for S+B model (needed for expected p0 values) int printLevel = optHT.printLevel; bool generateBinned = optHT.generateBinned; // for binned generation - bool useProof = optHT.useProof; // use Proof bool enableDetOutput = optHT.enableDetailedOutput; // Other Parameter to pass in tutorial @@ -372,12 +370,6 @@ void StandardHypoTestDemo(const char *infile = "", const char *workspaceName = " if (generateBinned) sampler->SetGenerateBinned(generateBinned); - // use PROOF - if (useProof) { - ProofConfig pc(*w, 0, "", kFALSE); - sampler->SetProofConfig(&pc); // enable proof - } - // set the test statistic if (testStatType == 0) sampler->SetTestStatistic(slrts); diff --git a/tutorials/roostats/StandardHypoTestInvDemo.C b/tutorials/roostats/StandardHypoTestInvDemo.C index bb3fe170d5fd4..4a0bea4c73bdb 100644 --- a/tutorials/roostats/StandardHypoTestInvDemo.C +++ b/tutorials/roostats/StandardHypoTestInvDemo.C @@ -83,8 +83,6 @@ struct HypoTestInvOptions { // to their nominal values) double nToysRatio = 2; // ratio Ntoys S+b/ntoysB double maxPOI = -1; // max value used of POI (in case of auto scan) - bool useProof = false; // use Proof Lite when using toys (for freq or hybrid) - int nworkers = 0; // number of worker for ProofLite (default use all available cores) bool enableDetailedOutput = false; // enable detailed output with all fit information for each toys (output will be written in result file) bool rebuild = false; // re-do extra toys for computing expected limits and rebuild test stat @@ -97,7 +95,6 @@ struct HypoTestInvOptions { // Otherwise the rebuild will be performed using int initialFit = -1; // do a first fit to the model (-1 : default, 0 skip fit, 1 do always fit) int randomSeed = -1; // random seed (if = -1: use default value, if = 0 always random ) - // NOTE: Proof uses automatically a random seed int nAsimovBins = 0; // number of bins in observables used for Asimov data sets (0 is the default and it is given by // workspace, typically is 100) @@ -144,11 +141,9 @@ private: bool mOptimize; bool mUseVectorStore; bool mGenerateBinned; - bool mUseProof; bool mRebuild; bool mReuseAltToys; bool mEnableDetOutput; - int mNWorkers; int mNToyToRebuild; int mRebuildParamValues; int mPrintLevel; @@ -167,7 +162,7 @@ private: RooStats::HypoTestInvTool::HypoTestInvTool() : mPlotHypoTestResult(true), mWriteResult(false), mOptimize(true), mUseVectorStore(true), mGenerateBinned(false), - mUseProof(false), mEnableDetOutput(false), mRebuild(false), mReuseAltToys(false), mNWorkers(4), + mEnableDetOutput(false), mRebuild(false), mReuseAltToys(false), mNToyToRebuild(100), mRebuildParamValues(0), mPrintLevel(0), mInitialFit(-1), mRandomSeed(-1), mNToysRatio(2), mMaxPoi(-1), mAsimovBins(0), mMassValue(""), mMinimizerType(""), mResultFileName() { @@ -191,8 +186,6 @@ void RooStats::HypoTestInvTool::SetParameter(const char *name, bool value) mUseVectorStore = value; if (s_name.find("GenerateBinned") != std::string::npos) mGenerateBinned = value; - if (s_name.find("UseProof") != std::string::npos) - mUseProof = value; if (s_name.find("EnableDetailedOutput") != std::string::npos) mEnableDetOutput = value; if (s_name.find("Rebuild") != std::string::npos) @@ -211,8 +204,6 @@ void RooStats::HypoTestInvTool::SetParameter(const char *name, int value) std::string s_name(name); - if (s_name.find("NWorkers") != std::string::npos) - mNWorkers = value; if (s_name.find("NToyToRebuild") != std::string::npos) mNToyToRebuild = value; if (s_name.find("RebuildParamValues") != std::string::npos) @@ -305,7 +296,6 @@ void StandardHypoTestInvDemo(const char *infile = nullptr, const char *wsName = extra options are available as global parameters of the macro. They major ones are: plotHypoTestResult plot result of tests at each point (TS distributions) (default is true) - useProof use Proof (default is true) writeResult write result of scan (default is true) rebuild rebuild scan for expected limits (require extra toys) (default is false) generateBinned generate binned data sets for toys (default is false) - be careful not to activate with @@ -352,9 +342,7 @@ void StandardHypoTestInvDemo(const char *infile = nullptr, const char *wsName = calc.SetParameter("GenerateBinned", optHTInv.generateBinned); calc.SetParameter("NToysRatio", optHTInv.nToysRatio); calc.SetParameter("MaxPOI", optHTInv.maxPOI); - calc.SetParameter("UseProof", optHTInv.useProof); calc.SetParameter("EnableDetailedOutput", optHTInv.enableDetailedOutput); - calc.SetParameter("NWorkers", optHTInv.nworkers); calc.SetParameter("Rebuild", optHTInv.rebuild); calc.SetParameter("ReuseAltToys", optHTInv.reuseAltToys); calc.SetParameter("NToyToRebuild", optHTInv.nToyToRebuild); @@ -942,12 +930,6 @@ HypoTestInverterResult *RooStats::HypoTestInvTool::RunInverter(RooWorkspace *w, calc.UseCLs(useCLs); calc.SetVerbose(true); - // can speed up using proof-lite - if (mUseProof) { - ProofConfig pc(*w, mNWorkers, "", kFALSE); - toymcs->SetProofConfig(&pc); // enable proof - } - if (npoints > 0) { if (poimin > poimax) { // if no min/max given scan between MLE and +4 sigma @@ -1008,7 +990,6 @@ HypoTestInverterResult *RooStats::HypoTestInvTool::RunInverter(RooWorkspace *w, std::cout << "StandardHypoTestInvDemo: Initial parameters used for rebuilding: "; RooStats::PrintListContent(*allParams, std::cout); - calc.SetCloseProof(true); tw.Start(); SamplingDistribution *limDist = calc.GetUpperLimitDistribution(true, mNToyToRebuild); std::cout << "Time to rebuild distributions " << std::endl; diff --git a/tutorials/roostats/StandardTestStatDistributionDemo.C b/tutorials/roostats/StandardTestStatDistributionDemo.C index 9d9898836b75d..08557494714b1 100644 --- a/tutorials/roostats/StandardTestStatDistributionDemo.C +++ b/tutorials/roostats/StandardTestStatDistributionDemo.C @@ -48,9 +48,6 @@ using namespace RooFit; using namespace RooStats; -bool useProof = false; // flag to control whether to use Proof -int nworkers = 0; // number of workers (default use all available cores) - // ------------------------------------------------------- // The actual macro @@ -157,11 +154,6 @@ void StandardTestStatDistributionDemo(const char *infile = "", const char *works firstPOI->setVal(plcUpperLimit); // set POI value for generation sampler.SetParametersForTestStat(*mc->GetParametersOfInterest()); // set POI value for evaluation - if (useProof) { - ProofConfig pc(*w, nworkers, "", false); - sampler.SetProofConfig(&pc); // enable proof - } - firstPOI->setVal(plcUpperLimit); RooArgSet allParameters; allParameters.add(*mc->GetParametersOfInterest()); diff --git a/tutorials/roostats/TwoSidedFrequentistUpperLimitWithBands.C b/tutorials/roostats/TwoSidedFrequentistUpperLimitWithBands.C index 3417525353e99..3341b2e66e0ac 100644 --- a/tutorials/roostats/TwoSidedFrequentistUpperLimitWithBands.C +++ b/tutorials/roostats/TwoSidedFrequentistUpperLimitWithBands.C @@ -123,9 +123,6 @@ using namespace RooFit; using namespace RooStats; using std::cout, std::endl; -bool useProof = false; // flag to control whether to use Proof -int nworkers = 0; // number of workers (default use all available cores) - // ------------------------------------------------------- void TwoSidedFrequentistUpperLimitWithBands(const char *infile = "", const char *workspaceName = "combined", @@ -225,17 +222,6 @@ void TwoSidedFrequentistUpperLimitWithBands(const char *infile = "", const char cout << "Not sure what to do about this model" << endl; } - // We can use PROOF to speed things along in parallel - // However, the test statistic has to be installed on the workers - // so either turn off PROOF or include the modified test statistic - // in your $ROOTSYS/roofit/roostats/inc directory, - // add the additional line to the LinkDef.h file, - // and recompile root. - if (useProof) { - ProofConfig pc(*w, nworkers, "", false); - toymcsampler->SetProofConfig(&pc); // enable proof - } - if (mc->GetGlobalObservables()) { cout << "will use global observables for unconditional ensemble" << endl; mc->GetGlobalObservables()->Print(); From 0f8918577415e57503f253f2394e8e1755458b36 Mon Sep 17 00:00:00 2001 From: ferdymercury Date: Mon, 9 Dec 2024 14:53:02 +0100 Subject: [PATCH 02/12] strlen optimization: use constexpr instead of runtime-constant strlen cannot be a constexpr in all platforms otherwise. --- core/base/src/TUrl.cxx | 2 +- core/foundation/src/TClassEdit.cxx | 63 ++++++++++--------- core/meta/src/TClass.cxx | 2 +- core/metacling/src/TCling.cxx | 2 +- graf3d/gl/src/gl2ps.cxx | 7 ++- hist/hist/src/HFitImpl.cxx | 5 +- .../lib/Interpreter/ForwardDeclPrinter.cpp | 2 +- io/io/src/TFile.cxx | 4 +- io/io/src/TMakeProject.cxx | 2 +- io/io/src/TStreamerInfo.cxx | 8 +-- io/sql/src/TSQLObjectData.cxx | 2 +- main/src/h2root.cxx | 2 +- main/src/hadd.cxx | 2 +- tree/tree/src/TBranch.cxx | 2 +- tree/tree/src/TBranchElement.cxx | 9 +-- tree/tree/src/TBranchSTL.cxx | 7 ++- tree/tree/src/TTree.cxx | 2 +- tree/treeplayer/src/TFormLeafInfo.cxx | 2 +- tree/treeplayer/src/TTreePlayer.cxx | 29 ++++----- 19 files changed, 82 insertions(+), 72 deletions(-) diff --git a/core/base/src/TUrl.cxx b/core/base/src/TUrl.cxx index 91821d381e016..d06c72c1b4ce2 100644 --- a/core/base/src/TUrl.cxx +++ b/core/base/src/TUrl.cxx @@ -195,7 +195,7 @@ void TUrl::SetUrl(const char *url, Bool_t defaultIsFile) // allow url of form: "proto://" } else { if (defaultIsFile) { - const std::size_t bufferSize = strlen("file:") + strlen(u0) + 1; + const std::size_t bufferSize = std::char_traits::length("file:") + strlen(u0) + 1; char *newu = new char [bufferSize]; snprintf(newu, bufferSize, "file:%s", u0); delete [] u0; diff --git a/core/foundation/src/TClassEdit.cxx b/core/foundation/src/TClassEdit.cxx index bbba888b3c78d..5d7a8872ab7ff 100644 --- a/core/foundation/src/TClassEdit.cxx +++ b/core/foundation/src/TClassEdit.cxx @@ -624,7 +624,8 @@ bool TClassEdit::IsDefAlloc(const char *allocname, const char *classname) string_view a( allocname ); // In Windows, allocname might be 'class const std::allocator', // (never 'const class ...'), so we start by stripping the 'class ', if any - constexpr static int clalloclen = std::char_traits::length("class "); + constexpr auto length = std::char_traits::length; + constexpr static int clalloclen = length("class "); if (a.compare(0,clalloclen,"class ") == 0) { a.remove_prefix(clalloclen); } @@ -634,7 +635,7 @@ bool TClassEdit::IsDefAlloc(const char *allocname, const char *classname) if (a=="__default_alloc_template") return true; if (a=="__malloc_alloc_template<0>") return true; - constexpr static int alloclen = std::char_traits::length("allocator<"); + constexpr static int alloclen = length("allocator<"); if (a.compare(0,alloclen,"allocator<") != 0) { return false; } @@ -683,7 +684,8 @@ bool TClassEdit::IsDefAlloc(const char *allocname, string_view a( allocname ); RemoveStd(a); - constexpr static int alloclen = std::char_traits::length("allocator<"); + constexpr auto length = std::char_traits::length; + constexpr static int alloclen = length("allocator<"); if (a.compare(0,alloclen,"allocator<") != 0) { return false; } @@ -691,7 +693,7 @@ bool TClassEdit::IsDefAlloc(const char *allocname, RemoveStd(a); - constexpr static int pairlen = std::char_traits::length("pair<"); + constexpr static int pairlen = length("pair<"); if (a.compare(0,pairlen,"pair<") != 0) { return false; } @@ -1082,8 +1084,10 @@ int TClassEdit::GetSplit(const char *type, vector& output, int &nestedLo if (full.compare(offset, 5, "std::") == 0) { offset += 5; } - static const char* char_traits_s = "char_traits"; - static const unsigned int char_traits_len = strlen(char_traits_s); + constexpr auto char_traits_s = "char_traits"; + // or + // static constexpr char const* const char_traits_s = "char_traits"; + static constexpr unsigned int char_traits_len = std::char_traits::length(char_traits_s); if (full.compare(offset, char_traits_len, char_traits_s) == 0) { offset += char_traits_len; if ( full[offset] == '>') { @@ -1347,7 +1351,7 @@ bool TClassEdit::IsInterpreterDetail(const char *type) bool TClassEdit::IsSTLBitset(const char *classname) { size_t offset = StdLen(classname); - if ( strncmp(classname+offset,"bitset<",strlen("bitset<"))==0) return true; + if ( strncmp(classname+offset,"bitset<",std::char_traits::length("bitset<"))==0) return true; return false; } @@ -1424,32 +1428,33 @@ int TClassEdit::IsSTLCont(const char *type, int testAlloc) bool TClassEdit::IsStdClass(const char *classname) { + constexpr auto length = std::char_traits::length; classname += StdLen( classname ); if ( strcmp(classname,"string")==0 ) return true; - if ( strncmp(classname,"bitset<",strlen("bitset<"))==0) return true; + if ( strncmp(classname,"bitset<",length("bitset<"))==0) return true; if ( IsStdPair(classname) ) return true; if ( strcmp(classname,"allocator")==0) return true; - if ( strncmp(classname,"allocator<",strlen("allocator<"))==0) return true; - if ( strncmp(classname,"greater<",strlen("greater<"))==0) return true; - if ( strncmp(classname,"less<",strlen("less<"))==0) return true; - if ( strncmp(classname,"equal_to<",strlen("equal_to<"))==0) return true; - if ( strncmp(classname,"hash<",strlen("hash<"))==0) return true; - if ( strncmp(classname,"auto_ptr<",strlen("auto_ptr<"))==0) return true; - - if ( strncmp(classname,"vector<",strlen("vector<"))==0) return true; - if ( strncmp(classname,"list<",strlen("list<"))==0) return true; - if ( strncmp(classname,"forward_list<",strlen("forward_list<"))==0) return true; - if ( strncmp(classname,"deque<",strlen("deque<"))==0) return true; - if ( strncmp(classname,"map<",strlen("map<"))==0) return true; - if ( strncmp(classname,"multimap<",strlen("multimap<"))==0) return true; - if ( strncmp(classname,"set<",strlen("set<"))==0) return true; - if ( strncmp(classname,"multiset<",strlen("multiset<"))==0) return true; - if ( strncmp(classname,"unordered_set<",strlen("unordered_set<"))==0) return true; - if ( strncmp(classname,"unordered_multiset<",strlen("unordered_multiset<"))==0) return true; - if ( strncmp(classname,"unordered_map<",strlen("unordered_map<"))==0) return true; - if ( strncmp(classname,"unordered_multimap<",strlen("unordered_multimap<"))==0) return true; - if ( strncmp(classname,"bitset<",strlen("bitset<"))==0) return true; - if ( strncmp(classname,"ROOT::VecOps::RVec<",strlen("ROOT::VecOps::RVec<"))==0) return true; + if ( strncmp(classname,"allocator<",length("allocator<"))==0) return true; + if ( strncmp(classname,"greater<",length("greater<"))==0) return true; + if ( strncmp(classname,"less<",length("less<"))==0) return true; + if ( strncmp(classname,"equal_to<",length("equal_to<"))==0) return true; + if ( strncmp(classname,"hash<",length("hash<"))==0) return true; + if ( strncmp(classname,"auto_ptr<",length("auto_ptr<"))==0) return true; + + if ( strncmp(classname,"vector<",length("vector<"))==0) return true; + if ( strncmp(classname,"list<",length("list<"))==0) return true; + if ( strncmp(classname,"forward_list<",length("forward_list<"))==0) return true; + if ( strncmp(classname,"deque<",length("deque<"))==0) return true; + if ( strncmp(classname,"map<",length("map<"))==0) return true; + if ( strncmp(classname,"multimap<",length("multimap<"))==0) return true; + if ( strncmp(classname,"set<",length("set<"))==0) return true; + if ( strncmp(classname,"multiset<",length("multiset<"))==0) return true; + if ( strncmp(classname,"unordered_set<",length("unordered_set<"))==0) return true; + if ( strncmp(classname,"unordered_multiset<",length("unordered_multiset<"))==0) return true; + if ( strncmp(classname,"unordered_map<",length("unordered_map<"))==0) return true; + if ( strncmp(classname,"unordered_multimap<",length("unordered_multimap<"))==0) return true; + if ( strncmp(classname,"bitset<",length("bitset<"))==0) return true; + if ( strncmp(classname,"ROOT::VecOps::RVec<",length("ROOT::VecOps::RVec<"))==0) return true; return false; } diff --git a/core/meta/src/TClass.cxx b/core/meta/src/TClass.cxx index 990ccf2842292..80c8af356f5c2 100644 --- a/core/meta/src/TClass.cxx +++ b/core/meta/src/TClass.cxx @@ -3220,7 +3220,7 @@ TClass *TClass::GetClass(const char *name, Bool_t load, Bool_t silent, size_t hi return pairinfo->GetClass(); } else { // Check if we have an STL container that might provide it. - static const size_t slen = strlen("pair"); + static constexpr size_t slen = std::char_traits::length("pair"); static const char *associativeContainer[] = { "map", "unordered_map", "multimap", "unordered_multimap", "set", "unordered_set", "multiset", "unordered_multiset" }; for(auto contname : associativeContainer) { diff --git a/core/metacling/src/TCling.cxx b/core/metacling/src/TCling.cxx index d5cb7b1745f2d..db04d5110daa3 100644 --- a/core/metacling/src/TCling.cxx +++ b/core/metacling/src/TCling.cxx @@ -4096,7 +4096,7 @@ void TCling::SetClassInfo(TClass* cl, Bool_t reload, Bool_t silent) // Handle the special case of 'tuple' where we ignore the real implementation // details and just overlay a 'simpler'/'simplistic' version that is easy // for the I/O to understand and handle. - if (strncmp(cl->GetName(),"tuple<",strlen("tuple<"))==0) { + if (strncmp(cl->GetName(),"tuple<",std::char_traits::length("tuple<"))==0) { if (!reload) name = AlternateTuple(cl->GetName(), fInterpreter->getLookupHelper(), silent); if (reload || name.empty()) { diff --git a/graf3d/gl/src/gl2ps.cxx b/graf3d/gl/src/gl2ps.cxx index 271e12fd0e914..126fd69885675 100644 --- a/graf3d/gl/src/gl2ps.cxx +++ b/graf3d/gl/src/gl2ps.cxx @@ -64,6 +64,7 @@ #include #include #include +#include #if defined(GL2PS_HAVE_ZLIB) #include @@ -4428,10 +4429,10 @@ static int gl2psPrintPDFShaderMask(int obj, int childobj) obj, (int)gl2ps->viewport[0], (int)gl2ps->viewport[1], (int)gl2ps->viewport[2], (int)gl2ps->viewport[3]); - + constexpr auto length = std::char_traits::length; len = (childobj>0) - ? strlen("/TrSh sh\n") + (int)log10((double)childobj)+1 - : strlen("/TrSh0 sh\n"); + ? length("/TrSh sh\n") + (int)log10((double)childobj)+1 + : length("/TrSh0 sh\n"); offs += fprintf(gl2ps->stream, "/Length %d\n" diff --git a/hist/hist/src/HFitImpl.cxx b/hist/hist/src/HFitImpl.cxx index 6f3f2a2e8e8cc..054b866d8e82b 100644 --- a/hist/hist/src/HFitImpl.cxx +++ b/hist/hist/src/HFitImpl.cxx @@ -760,14 +760,15 @@ void ROOT::Fit::FitOptionsMake(EFitObjectType type, const char *option, Foption_ //for robust fitting, see if # of good points is defined // decode parameters for robust fitting Double_t h=0; + constexpr auto length = std::char_traits::length; if (opt.Contains("H=0.")) { int start = opt.Index("H=0."); - int numpos = start + strlen("H=0."); + int numpos = start + length("H=0."); int numlen = 0; int len = opt.Length(); while( (numpos+numlengetASTContext().getLangOpts().Modules && llvm::StringRef(includeText).starts_with("include ")) { - includeText += strlen("include "); + includeText += std::char_traits::length("include "); } assert((includeText[0] == '<' || includeText[0] == '"') && diff --git a/io/io/src/TFile.cxx b/io/io/src/TFile.cxx index 54ed25c2470f4..cee4d1ba2f395 100644 --- a/io/io/src/TFile.cxx +++ b/io/io/src/TFile.cxx @@ -3086,7 +3086,7 @@ void TFile::MakeProject(const char *dirname, const char * /*classes*/, if (info->IsA() != TStreamerInfo::Class()) { continue; } - if (strncmp(info->GetName(), "auto_ptr<", strlen("auto_ptr<")) == 0) { + if (strncmp(info->GetName(), "auto_ptr<", std::char_traits::length("auto_ptr<")) == 0) { continue; } TClass *cl = TClass::GetClass(info->GetName()); @@ -4129,7 +4129,7 @@ TFile *TFile::Open(const char *url, Option_t *options, const char *ftitle, TString opts(options); Int_t ito = opts.Index("TIMEOUT="); if (ito != kNPOS) { - TString sto = opts(ito + strlen("TIMEOUT="), opts.Length()); + TString sto = opts(ito + std::char_traits::length("TIMEOUT="), opts.Length()); while (!(sto.IsDigit()) && !(sto.IsNull())) { sto.Remove(sto.Length()-1,1); } if (!(sto.IsNull())) { // Timeout in millisecs diff --git a/io/io/src/TMakeProject.cxx b/io/io/src/TMakeProject.cxx index 8017e82c63610..6592c03917318 100644 --- a/io/io/src/TMakeProject.cxx +++ b/io/io/src/TMakeProject.cxx @@ -524,7 +524,7 @@ UInt_t TMakeProject::GenerateIncludeForTemplate(FILE *fp, const char *clname, ch } else if (TClassEdit::IsStdPair(incName)) { AddInclude(fp, "utility", kTRUE, inclist); ninc += GenerateIncludeForTemplate(fp, incName, inclist, forward, extrainfos); - } else if (strncmp(incName.Data(), "auto_ptr<", strlen("auto_ptr<")) == 0) { + } else if (strncmp(incName.Data(), "auto_ptr<", std::char_traits::length("auto_ptr<")) == 0) { AddInclude(fp, "memory", kTRUE, inclist); ninc += GenerateIncludeForTemplate(fp, incName, inclist, forward, extrainfos); } else if (TClassEdit::IsStdClass(incName)) { diff --git a/io/io/src/TStreamerInfo.cxx b/io/io/src/TStreamerInfo.cxx index 534ae10f2cc49..de31d5ac7a9ee 100644 --- a/io/io/src/TStreamerInfo.cxx +++ b/io/io/src/TStreamerInfo.cxx @@ -3836,7 +3836,7 @@ void TStreamerInfo::GenerateDeclaration(FILE *fp, FILE *sfp, const TList *subCla // nothing to do. break; } - } else if (strncmp(enamebasic.Data(), "auto_ptr<", strlen("auto_ptr<")) == 0) { + } else if (strncmp(enamebasic.Data(), "auto_ptr<", std::char_traits::length("auto_ptr<")) == 0) { enamebasic = TMakeProject::UpdateAssociativeToVector(enamebasic); } @@ -3986,7 +3986,7 @@ UInt_t TStreamerInfo::GenerateIncludes(FILE *fp, char *inclist, const TList *ext } if (TClassEdit::IsStdPair(element->GetTypeName())) { TMakeProject::AddInclude( fp, "utility", kTRUE, inclist); - } else if (strncmp(element->GetTypeName(),"auto_ptr<",strlen("auto_ptr<"))==0) { + } else if (strncmp(element->GetTypeName(),"auto_ptr<",std::char_traits::length("auto_ptr<"))==0) { TMakeProject::AddInclude( fp, "memory", kTRUE, inclist); } else { TString incName( include, strlen(include)-1 ); @@ -4011,7 +4011,7 @@ Int_t TStreamerInfo::GenerateHeaderFile(const char *dirname, const TList *subCla // if (fClassVersion == -4) return 0; if ((fClass && fClass->GetCollectionType()) || TClassEdit::IsSTLCont(GetName())) return 0; if (TClassEdit::IsStdPair(GetName())) return 0; - if (strncmp(GetName(),"auto_ptr<",strlen("auto_ptr<"))==0) return 0; + if (strncmp(GetName(),"auto_ptr<",std::char_traits::length("auto_ptr<"))==0) return 0; TClass *cl = TClass::GetClass(GetName()); if (cl) { @@ -5838,7 +5838,7 @@ TVirtualStreamerInfo *TStreamerInfo::GenerateInfoForPair(const std::string &firs TVirtualStreamerInfo *TStreamerInfo::GenerateInfoForPair(const std::string &pairclassname, bool silent, size_t hint_pair_offset, size_t hint_pair_size) { - const static int pairlen = strlen("pair<"); + const static int pairlen = std::char_traits::length("pair<"); if (pairclassname.compare(0, pairlen, "pair<") != 0) { if (!silent) Error("GenerateInfoForPair", "The class name passed is not a pair: %s", pairclassname.c_str()); diff --git a/io/sql/src/TSQLObjectData.cxx b/io/sql/src/TSQLObjectData.cxx index cb3f7e7f2ad0a..5c820dda4ecb8 100644 --- a/io/sql/src/TSQLObjectData.cxx +++ b/io/sql/src/TSQLObjectData.cxx @@ -238,7 +238,7 @@ Bool_t TSQLObjectData::ExtractBlobValues() fBlobTypeName = name; } else { fBlobPrefixName = name; - separ += strlen(":"); // SQLNameSeparator() + separ += std::char_traits::length(":"); // SQLNameSeparator() fBlobTypeName = separ; } diff --git a/main/src/h2root.cxx b/main/src/h2root.cxx index b374c16c2ffaf..79a1584404ae2 100644 --- a/main/src/h2root.cxx +++ b/main/src/h2root.cxx @@ -294,7 +294,7 @@ int main(int argc, char **argv) if (argc > 2) { file_out=argv[2]; } else { - Int_t nchf = strlen(file_in)+strlen(".root")+1; + Int_t nchf = strlen(file_in)+std::char_traits::length(".root")+1; file_out= new char[nchf]; strlcpy(file_out,file_in,nchf); char *dot = strrchr(file_out,'.'); diff --git a/main/src/hadd.cxx b/main/src/hadd.cxx index 933859f3941a6..6934df8944bdd 100644 --- a/main/src/hadd.cxx +++ b/main/src/hadd.cxx @@ -211,7 +211,7 @@ int main( int argc, char **argv ) ++ffirst; } else if ( strcmp(argv[a],"-cachesize=") == 0 ) { int size; - static const size_t arglen = strlen("-cachesize="); + static constexpr size_t arglen = std::char_traits::length("-cachesize="); auto parseResult = ROOT::FromHumanReadableSize(argv[a]+arglen,size); if (parseResult == ROOT::EFromHumanReadableSize::kParseFail) { std::cerr << "Error: could not parse the cache size passed after -cachesize: " diff --git a/tree/tree/src/TBranch.cxx b/tree/tree/src/TBranch.cxx index 0e6411343ffc8..acfda89f1677c 100644 --- a/tree/tree/src/TBranch.cxx +++ b/tree/tree/src/TBranch.cxx @@ -2425,7 +2425,7 @@ void TBranch::Print(Option_t *option) const } Printf("*Baskets :%9d : Basket Size=%11d bytes Compression= %6.2f *",fWriteBasket,fBasketSize,cx); - if (strncmp(option,"basketsInfo",strlen("basketsInfo"))==0) { + if (strncmp(option,"basketsInfo",std::char_traits::length("basketsInfo"))==0) { Int_t nbaskets = fWriteBasket; for (Int_t i=0;i::length; Int_t nbranches = fBranches.GetEntriesFast(); - if (strncmp(option,"debugAddress",strlen("debugAddress"))==0) { - if (strlen(option)==strlen("debugAddress")) { + if (strncmp(option,"debugAddress",length("debugAddress"))==0) { + if (strlen(option)==length("debugAddress")) { Printf("%-24s %-16s %2s %4s %-16s %-16s %8s %8s %s %s\n", "Branch Name", "Streamer Class", "ID", "Type", "Class", "Parent", "pOffset", "fOffset", "fObject", "fOnfileObject"); } @@ -3859,7 +3860,7 @@ void TBranchElement::Print(Option_t* option) const } return; } - if (strncmp(option,"debugInfo",strlen("debugInfo"))==0) { + if (strncmp(option,"debugInfo",length("debugInfo"))==0) { Printf("Branch %s uses:",GetName()); if (fID>=0) { // GetInfoImp()->GetElement(fID)->ls(); @@ -3892,7 +3893,7 @@ void TBranchElement::Print(Option_t* option) const if (fFillActionSequence) fFillActionSequence->Print(option); } TString suboption = "debugInfoSub"; - suboption += (option+strlen("debugInfo")); + suboption += (option+length("debugInfo")); for (Int_t i = 0; i < nbranches; ++i) { TBranchElement* subbranch = (TBranchElement*)fBranches.At(i); subbranch->Print(suboption); diff --git a/tree/tree/src/TBranchSTL.cxx b/tree/tree/src/TBranchSTL.cxx index 8ec498a649333..9dd82119b86c0 100644 --- a/tree/tree/src/TBranchSTL.cxx +++ b/tree/tree/src/TBranchSTL.cxx @@ -602,8 +602,9 @@ bool TBranchSTL::IsFolder() const void TBranchSTL::Print(const char *option) const { - if (strncmp(option,"debugAddress",strlen("debugAddress"))==0) { - if (strlen(GetName())>24) Printf("%-24s\n%-24s ", GetName(),""); + constexpr auto length = std::char_traits::length; + if (strncmp(option,"debugAddress",length("debugAddress"))==0) { + if (length(GetName())>24) Printf("%-24s\n%-24s ", GetName(),""); else Printf("%-24s ", GetName()); TBranchElement *parent = dynamic_cast(GetMother()->GetSubBranch(this)); @@ -620,7 +621,7 @@ void TBranchSTL::Print(const char *option) const TBranch *br = (TBranch *)fBranches.UncheckedAt(i); br->Print("debugAddressSub"); } - } else if (strncmp(option,"debugInfo",strlen("debugInfo"))==0) { + } else if (strncmp(option,"debugInfo",length("debugInfo"))==0) { Printf("Branch %s uses:\n",GetName()); if (fID>=0) { GetInfo()->GetElement(fID)->ls(); diff --git a/tree/tree/src/TTree.cxx b/tree/tree/src/TTree.cxx index 41e53481ffe25..fdd0158699aea 100644 --- a/tree/tree/src/TTree.cxx +++ b/tree/tree/src/TTree.cxx @@ -7246,7 +7246,7 @@ void TTree::Print(Option_t* option) const if (!option) option = ""; - if (strncmp(option,"clusters",strlen("clusters"))==0) { + if (strncmp(option,"clusters",std::char_traits::length("clusters"))==0) { Printf("%-16s %-16s %-16s %8s %20s", "Cluster Range #", "Entry Start", "Last Entry", "Size", "Number of clusters"); Int_t index= 0; diff --git a/tree/treeplayer/src/TFormLeafInfo.cxx b/tree/treeplayer/src/TFormLeafInfo.cxx index 0308623957370..2438eff891039 100644 --- a/tree/treeplayer/src/TFormLeafInfo.cxx +++ b/tree/treeplayer/src/TFormLeafInfo.cxx @@ -1020,7 +1020,7 @@ TFormLeafInfoNumerical::TFormLeafInfoNumerical(TVirtualCollectionProxy *collecti if (fKind == TStreamerInfo::kOffsetL + TStreamerInfo::kChar) { // Could be a bool if (strcmp( collection->GetCollectionClass()->GetName(), "vector") == 0 - || strncmp( collection->GetCollectionClass()->GetName(), "bitset<", strlen("bitset<") ) ==0 ) { + || strncmp( collection->GetCollectionClass()->GetName(), "bitset<", std::char_traits::length("bitset<") ) ==0 ) { fIsBool = true; fKind = (EDataType)18; } diff --git a/tree/treeplayer/src/TTreePlayer.cxx b/tree/treeplayer/src/TTreePlayer.cxx index 036153641c6c7..0fe464bd345cb 100644 --- a/tree/treeplayer/src/TTreePlayer.cxx +++ b/tree/treeplayer/src/TTreePlayer.cxx @@ -797,6 +797,7 @@ Int_t TTreePlayer::MakeClass(const char *classname, const char *option) fprintf(fp,"\n// Header file for the classes stored in the TTree if any.\n"); TList listOfHeaders; listOfHeaders.SetOwner(); + constexpr auto length = std::char_traits::length; for (l=0;lUncheckedAt(l); TBranch *branch = leaf->GetBranch(); @@ -812,11 +813,11 @@ Int_t TTreePlayer::MakeClass(const char *classname, const char *option) fprintf(fp,"#include <%s>\n",declfile+precstl_len); listOfHeaders.Add(new TNamed(cl->GetName(),declfile+precstl_len)); } else if (strncmp(declfile,"/usr/include/",13) == 0) { - fprintf(fp,"#include <%s>\n",declfile+strlen("/include/c++/")); - listOfHeaders.Add(new TNamed(cl->GetName(),declfile+strlen("/include/c++/"))); + fprintf(fp,"#include <%s>\n",declfile+length("/include/c++/")); + listOfHeaders.Add(new TNamed(cl->GetName(),declfile+length("/include/c++/"))); } else if (strstr(declfile,"/include/c++/") != nullptr) { - fprintf(fp,"#include <%s>\n",declfile+strlen("/include/c++/")); - listOfHeaders.Add(new TNamed(cl->GetName(),declfile+strlen("/include/c++/"))); + fprintf(fp,"#include <%s>\n",declfile+length("/include/c++/")); + listOfHeaders.Add(new TNamed(cl->GetName(),declfile+length("/include/c++/"))); } else if (strncmp(declfile,rootinclude,rootinclude_len) == 0) { fprintf(fp,"#include <%s>\n",declfile+rootinclude_len); listOfHeaders.Add(new TNamed(cl->GetName(),declfile+rootinclude_len)); @@ -2359,7 +2360,7 @@ void TTreePlayer::RecursiveRemove(TObject *obj) } //////////////////////////////////////////////////////////////////////////////// -/// \brief Loop on Tree and print entries passing selection. Interactive +/// \brief Loop on Tree and print entries passing selection. Interactive /// pagination break is on by default. /// \param varexp If varexp is 0 (or "") then print only first 8 columns. /// If varexp = "*" print all columns. Otherwise a columns selection can @@ -2449,7 +2450,7 @@ Long64_t TTreePlayer::Scan(const char *varexp, const char *selection, Option_t * option, Long64_t nentries, Long64_t firstentry) { - + constexpr auto length = std::char_traits::length; TString opt = option; opt.ToLower(); UInt_t ui; @@ -2461,23 +2462,23 @@ Long64_t TTreePlayer::Scan(const char *varexp, const char *selection, if (opt.Contains("lenmax=")) { int start = opt.Index("lenmax="); - int numpos = start + strlen("lenmax="); + int numpos = start + length("lenmax="); int numlen = 0; int len = opt.Length(); while( (numpos+numlen Date: Mon, 9 Dec 2024 12:36:00 +0100 Subject: [PATCH 03/12] Many fixes in TFileDrawMap 1. Draw frame histogram and object only once 2. Use TH2 as frame histogram to properly handle zooming 3. In Paint method paint only TFileDrawMap itself 4. Keep generated branch colors to make reproducible painting 5. Make non-persistent TFile pointer, otherwise fails when reading 5. --- tree/treeplayer/inc/TFileDrawMap.h | 20 ++++++----- tree/treeplayer/src/TFileDrawMap.cxx | 53 ++++++++++------------------ 2 files changed, 29 insertions(+), 44 deletions(-) diff --git a/tree/treeplayer/inc/TFileDrawMap.h b/tree/treeplayer/inc/TFileDrawMap.h index c35873cbb6290..fd47f3992c75a 100644 --- a/tree/treeplayer/inc/TFileDrawMap.h +++ b/tree/treeplayer/inc/TFileDrawMap.h @@ -23,7 +23,9 @@ #include "TNamed.h" -class TH1; +#include + +class TH2; class TFile; class TDirectory; class TBox; @@ -32,12 +34,12 @@ class TBranch; class TFileDrawMap : public TNamed { protected: - TFile *fFile; ///< Pointer to the file - TH1 *fFrame; ///< Histogram used to draw the map frame - TString fKeys; ///< List of keys - TString fOption; ///< Drawing options - Int_t fXsize; ///< Size in bytes of X axis - Int_t fYsize; ///< Size in K/Mbytes of Y axis + TFile *fFile = nullptr; /// fBranchColors; ///GetEND()/fXsize); + + fFrame = new TH2D("hmapframe","",100,0,fXsize,100,0,fYsize); fFrame->SetDirectory(nullptr); fFrame->SetBit(TH1::kNoStats); fFrame->SetBit(kCanDelete); - fFrame->SetMinimum(0); if (fXsize > 1000) { fFrame->GetYaxis()->SetTitle("MBytes"); } else { fFrame->GetYaxis()->SetTitle("KBytes"); } fFrame->GetXaxis()->SetTitle("Bytes"); - fYsize = 1 + Int_t(file->GetEND()/fXsize); - fFrame->SetMaximum(fYsize); - fFrame->GetYaxis()->SetLimits(0,fYsize); - //bool show = false; - if (gPad) { + if (gPad) gPad->Clear(); - //show = gPad->GetCanvas()->GetShowEventStatus(); - } + + fFrame->Draw("axis"); Draw(); - if (gPad) { - //if (!show) gPad->GetCanvas()->ToggleEventStatus(); + + if (gPad) gPad->Update(); - } } //////////////////////////////////////////////////////////////////////////////// @@ -488,22 +482,8 @@ void TFileDrawMap::InspectObject() void TFileDrawMap::Paint(Option_t *) { - // draw map frame - if (!fOption.Contains("same")) { - gPad->Clear(); - //just in case axis Y has been unzoomed - if (fFrame->GetMaximumStored() < -1000) { - fFrame->SetMaximum(fYsize+1); - fFrame->SetMinimum(0); - fFrame->GetYaxis()->SetLimits(0,fYsize+1); - } - fFrame->Paint(); - } - //draw keys PaintDir(fFile, fKeys.Data()); - - fFrame->Draw("sameaxis"); } //////////////////////////////////////////////////////////////////////////////// @@ -573,13 +553,15 @@ void TFileDrawMap::PaintDir(TDirectory *dir, const char *keys) if (cl && cl->InheritsFrom(TTree::Class())) { TTree *tree = (TTree*)gDirectory->Get(key->GetName()); TIter nextb(tree->GetListOfLeaves()); - TLeaf *leaf; - while ((leaf = (TLeaf*)nextb())) { + while (auto leaf = (TLeaf*)nextb()) { TBranch *branch = leaf->GetBranch(); color = branch->GetFillColor(); if (color == 0) { - gPad->IncrementPaletteColor(1, "pfc"); - color = gPad->NextPaletteColor(); + if (fBranchColors.find(branch) == fBranchColors.end()) { + gPad->IncrementPaletteColor(1, "pfc"); + fBranchColors[branch] = gPad->NextPaletteColor(); + } + color = fBranchColors[branch]; } box.SetFillColor(color); Int_t nbaskets = branch->GetMaxBaskets(); @@ -592,6 +574,7 @@ void TFileDrawMap::PaintDir(TDirectory *dir, const char *keys) } } } + // draw the box for Keys list box.SetFillColor(50); box.SetFillStyle(1001); From cc6a9616c6430acc343ca6ef18b417768f86f462 Mon Sep 17 00:00:00 2001 From: silverweed Date: Tue, 10 Dec 2024 11:18:19 +0100 Subject: [PATCH 04/12] [tut] make sure python tutorials write into separate files --- tutorials/hist/hist001_TH1_fillrandom.py | 2 +- tutorials/hist/hist002_TH1_fillrandom_userfunc.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tutorials/hist/hist001_TH1_fillrandom.py b/tutorials/hist/hist001_TH1_fillrandom.py index c7b0da4f16ae4..86b4bc228c44b 100644 --- a/tutorials/hist/hist001_TH1_fillrandom.py +++ b/tutorials/hist/hist001_TH1_fillrandom.py @@ -18,5 +18,5 @@ h1d.FillRandom("gaus", 10000) # Open a ROOT file and save the histogram -with ROOT.TFile.Open("fillrandom.root", "RECREATE") as myfile: +with ROOT.TFile.Open("fillrandom_py.root", "RECREATE") as myfile: myfile.WriteObject(h1d, h1d.GetName()) diff --git a/tutorials/hist/hist002_TH1_fillrandom_userfunc.py b/tutorials/hist/hist002_TH1_fillrandom_userfunc.py index 163bc8dcfb693..d9ac5009ab17e 100644 --- a/tutorials/hist/hist002_TH1_fillrandom_userfunc.py +++ b/tutorials/hist/hist002_TH1_fillrandom_userfunc.py @@ -34,7 +34,7 @@ h1d.FillRandom("sqroot", 10000) # Open a ROOT file and save the formula, function and histogram -with ROOT.TFile.Open("fillrandom.root", "RECREATE") as myFile: +with ROOT.TFile.Open("fillrandom_userfunc_py.root", "RECREATE") as myFile: myFile.WriteObject(form1, form1.GetName()) myFile.WriteObject(sqroot, sqroot.GetName()) myFile.WriteObject(h1d, h1d.GetName()) From 3d8b3fc53cae1e87df5e19d8dd15938a63851c89 Mon Sep 17 00:00:00 2001 From: Vassil Vassilev Date: Tue, 10 Dec 2024 15:58:10 +0000 Subject: [PATCH 05/12] Prepare for releasing cling v1.2 --- interpreter/cling/VERSION | 2 +- interpreter/cling/docs/ReleaseNotes.md | 32 ++++++++++++++------------ interpreter/cling/www/news.html | 4 ++++ 3 files changed, 22 insertions(+), 16 deletions(-) diff --git a/interpreter/cling/VERSION b/interpreter/cling/VERSION index 70f303689ffb8..5625e59da8873 100644 --- a/interpreter/cling/VERSION +++ b/interpreter/cling/VERSION @@ -1 +1 @@ -1.2~dev +1.2 diff --git a/interpreter/cling/docs/ReleaseNotes.md b/interpreter/cling/docs/ReleaseNotes.md index f720e85576e30..eeef93e83dd63 100644 --- a/interpreter/cling/docs/ReleaseNotes.md +++ b/interpreter/cling/docs/ReleaseNotes.md @@ -19,15 +19,17 @@ infrastructure are described first. External Dependencies --------------------- -* Upgrade to LLVM r0000000. +* Upgrade to LLVM18. Major New Features ------------------ -* A major new feature +* Improvements in stability Misc ---- -* A misc feature +* Better handling of llvm::Error +* Better integration with Clad +* Modulemap fixes Experimental Features --------------------- @@ -40,14 +42,7 @@ Jupyter Fixed Bugs ---------- -[ROOT-XXXX](https://sft.its.cern.ch/jira/browse/ROOT-XXXX) - - - +[16654](https://github.com/root-project/cling/issues/16654) +Devajith Valaparambil Sreeramaswamy (22) +Jonas Hahnfeld (18) +Jonas Rembser (2) +Bertrand Bellenot (2) +ferdymercury (1) +dbonner (1) +Vipul Cariappa (1) +Vassil Vassilev (1) +Fredrik (1) +Danilo Piparo (1) +Aaron Jomy (1) diff --git a/interpreter/cling/www/news.html b/interpreter/cling/www/news.html index 343c0be51c86f..52c792b3a3e5f 100644 --- a/interpreter/cling/www/news.html +++ b/interpreter/cling/www/news.html @@ -6,6 +6,10 @@

Latest News

+

Cling release 1.2 is out

+
Dec 10th, 2024
+

Read more

+

Cling release 1.1 is out

Aug 28th, 2023

Read more

From 736b402b0b5af4e5196bcaca6b5bb59e2c44e292 Mon Sep 17 00:00:00 2001 From: moneta Date: Tue, 10 Dec 2024 12:52:20 +0100 Subject: [PATCH 06/12] [tmva][pymva] Change default algorithm for AdaBoost to SAMME Since scikit version 1.4 the SAMME.R algorithm is deprecated and it has been removed since version 1.6. Change default to use SAMME See https://scikit-learn.org/1.5/modules/generated/sklearn.ensemble.AdaBoostClassifier.html --- tmva/pymva/src/MethodPyAdaBoost.cxx | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/tmva/pymva/src/MethodPyAdaBoost.cxx b/tmva/pymva/src/MethodPyAdaBoost.cxx index 0af7bfc7dd021..0ad1533b6b8be 100644 --- a/tmva/pymva/src/MethodPyAdaBoost.cxx +++ b/tmva/pymva/src/MethodPyAdaBoost.cxx @@ -67,7 +67,7 @@ MethodPyAdaBoost::MethodPyAdaBoost(const TString &jobName, fBaseEstimator("None"), fNestimators(50), fLearningRate(1.0), - fAlgorithm("SAMME.R"), + fAlgorithm("SAMME"), fRandomState("None") { } @@ -79,7 +79,7 @@ MethodPyAdaBoost::MethodPyAdaBoost(DataSetInfo &theData, fBaseEstimator("None"), fNestimators(50), fLearningRate(1.0), - fAlgorithm("SAMME.R"), + fAlgorithm("SAMME"), fRandomState("None") { } @@ -116,12 +116,13 @@ void MethodPyAdaBoost::DeclareOptions() ``learning_rate``. There is a trade-off between ``learning_rate`` and\ ``n_estimators``."); - DeclareOptionRef(fAlgorithm, "Algorithm", "{'SAMME', 'SAMME.R'}, optional (default='SAMME.R')\ + DeclareOptionRef(fAlgorithm, "Algorithm", "{'SAMME', 'SAMME.R'}, optional (default='SAMME')\ If 'SAMME.R' then use the SAMME.R real boosting algorithm.\ ``base_estimator`` must support calculation of class probabilities.\ If 'SAMME' then use the SAMME discrete boosting algorithm.\ The SAMME.R algorithm typically converges faster than SAMME,\ - achieving a lower test error with fewer boosting iterations."); + achieving a lower test error with fewer boosting iterations.\ + 'SAME.R' is deprecated since version 1.4 and removed since 1.6"); DeclareOptionRef(fRandomState, "RandomState", "int, RandomState instance or None, optional (default=None)\ If int, random_state is the seed used by the random number generator;\ @@ -309,11 +310,11 @@ std::vector MethodPyAdaBoost::GetMvaValues(Long64_t firstEvt, Long64_t Py_DECREF(result); if (logProgress) { - Log() << kINFO + Log() << kINFO << "Elapsed time for evaluation of " << nEvents << " events: " << timer.GetElapsedTime() << " " << Endl; } - + return mvaValues; } From d2e3a6a0393b64a7aba6fe584de22f3b31962c8c Mon Sep 17 00:00:00 2001 From: Vassil Vassilev Date: Tue, 10 Dec 2024 16:26:19 +0000 Subject: [PATCH 07/12] Bump cling version to 1.3~dev. --- interpreter/cling/VERSION | 2 +- interpreter/cling/docs/ReleaseNotes.md | 36 ++++++++++++-------------- 2 files changed, 18 insertions(+), 20 deletions(-) diff --git a/interpreter/cling/VERSION b/interpreter/cling/VERSION index 5625e59da8873..e7ad5767c1aeb 100644 --- a/interpreter/cling/VERSION +++ b/interpreter/cling/VERSION @@ -1 +1 @@ -1.2 +1.3~dev diff --git a/interpreter/cling/docs/ReleaseNotes.md b/interpreter/cling/docs/ReleaseNotes.md index eeef93e83dd63..d58cb4cfc7107 100644 --- a/interpreter/cling/docs/ReleaseNotes.md +++ b/interpreter/cling/docs/ReleaseNotes.md @@ -2,7 +2,7 @@ Introduction ============ This document contains the release notes for the interactive C++ interpreter -Cling, release 1.2. Cling is built on top of [Clang](http://clang.llvm.org) and +Cling, release 1.3. Cling is built on top of [Clang](http://clang.llvm.org) and [LLVM](http://llvm.org>) compiler infrastructure. Here we describe the status of Cling in some detail, including major improvements from the previous release and new feature work. @@ -10,7 +10,7 @@ improvements from the previous release and new feature work. Note that if you are reading this file from a git checkout or the main Cling web page, this document applies to the *next* release, not the current one. -What's New in Cling 1.2? +What's New in Cling 1.3? ======================== Some of the major new features and improvements to Cling are listed @@ -19,17 +19,15 @@ infrastructure are described first. External Dependencies --------------------- -* Upgrade to LLVM18. +* Upgrade to LLVM r0000000. Major New Features ------------------ -* Improvements in stability +* A major new feature Misc ---- -* Better handling of llvm::Error -* Better integration with Clad -* Modulemap fixes +* A misc feature Experimental Features --------------------- @@ -42,7 +40,14 @@ Jupyter Fixed Bugs ---------- -[16654](https://github.com/root-project/cling/issues/16654) +[ROOT-XXXX](https://sft.its.cern.ch/jira/browse/ROOT-XXXX) + + + From 3afd23195a5ae4ff729407477bbd58ae5f1e92d3 Mon Sep 17 00:00:00 2001 From: Sergey Linev Date: Mon, 9 Dec 2024 13:44:50 +0100 Subject: [PATCH 08/12] [webcanv] provide class and object name with TWebPainting Let create meningful header for context menu --- gui/webgui6/inc/TWebPainting.h | 10 +++++++++- gui/webgui6/src/TWebCanvas.cxx | 2 ++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/gui/webgui6/inc/TWebPainting.h b/gui/webgui6/inc/TWebPainting.h index cc274f9769149..e470e439b71e4 100644 --- a/gui/webgui6/inc/TWebPainting.h +++ b/gui/webgui6/inc/TWebPainting.h @@ -25,6 +25,8 @@ class TColor; class TWebPainting : public TObject { protected: + std::string fClassName; ///< class name of object produced this painting + std::string fObjectName; ///< object name std::string fOper; ///< list of operations, separated by semicolons Int_t fSize{0}; ///SetClassName(obj->ClassName()); + ps.GetPainting()->SetObjectName(obj->GetName()); gVirtualPS = masterps ? masterps : &ps; if (painter) painter->SetPainting(ps.GetPainting()); From f7df05b6a6617ff185529b4b978dd0f6f42f2e5d Mon Sep 17 00:00:00 2001 From: Sergey Linev Date: Tue, 10 Dec 2024 10:57:42 +0100 Subject: [PATCH 09/12] Better web canvas support in TFileDrawMap While normal events are not provided by web canvas, try at least use last click event to identify which part of file is clicked. In all methods use TString for strings manipulation --- tree/treeplayer/inc/TFileDrawMap.h | 3 +- tree/treeplayer/src/TFileDrawMap.cxx | 173 ++++++++++++++++----------- 2 files changed, 105 insertions(+), 71 deletions(-) diff --git a/tree/treeplayer/inc/TFileDrawMap.h b/tree/treeplayer/inc/TFileDrawMap.h index fd47f3992c75a..5696d11304b3d 100644 --- a/tree/treeplayer/inc/TFileDrawMap.h +++ b/tree/treeplayer/inc/TFileDrawMap.h @@ -47,6 +47,8 @@ class TFileDrawMap : public TNamed { virtual void PaintDir(TDirectory *dir, const char *keys); virtual TObject *GetObject(); + TString GetRecentInfo(); + public: TFileDrawMap(); TFileDrawMap(const TFile *file, const char *keys, Option_t *option = ""); @@ -56,7 +58,6 @@ class TFileDrawMap : public TNamed { Int_t DistancetoPrimitive(Int_t px, Int_t py) override; virtual void DrawObject(); // *MENU* virtual void DumpObject(); // *MENU* - void ExecuteEvent(Int_t event, Int_t px, Int_t py) override; char *GetObjectInfo(Int_t px, Int_t py) const override; virtual void InspectObject(); // *MENU* void Paint(Option_t *option) override; diff --git a/tree/treeplayer/src/TFileDrawMap.cxx b/tree/treeplayer/src/TFileDrawMap.cxx index 952e422b51f21..c686dcd773d28 100644 --- a/tree/treeplayer/src/TFileDrawMap.cxx +++ b/tree/treeplayer/src/TFileDrawMap.cxx @@ -135,6 +135,25 @@ TFileDrawMap::~TFileDrawMap() //delete fFrame; //should not be deleted (kCanDelete set) } +//////////////////////////////////////////////////////////////////////////////// +/// Returns info which corresponds to recent mouse position +/// In case of normal graphics it is object name +/// In case of web canvas use stored click event position + +TString TFileDrawMap::GetRecentInfo() +{ + TString info; + if (gPad && gPad->IsWeb()) { + // in case of web canvas one can try to use last click event + GetObjectInfoDir(fFile, gPad->GetEventX(), gPad->GetEventY(), info); + } else { + // last selected place stored as name + info = GetName(); + } + return info; +} + + //////////////////////////////////////////////////////////////////////////////// /// Show sequence of baskets reads for the list of baskets involved /// in the list of branches (separated by ",") @@ -146,27 +165,36 @@ TFileDrawMap::~TFileDrawMap() void TFileDrawMap::AnimateTree(const char *branches) { - TString ourbranches( GetName() ); - Ssiz_t pos = ourbranches.Index(", basket="); - if (pos == kNPOS) return; - ourbranches.Remove(pos); - pos = ourbranches.Index(", branch="); - if (pos == kNPOS) return; - ourbranches[pos] = 0; - - TTree *tree = (TTree*)fFile->Get(ourbranches.Data()); - if (!tree) return; - TString info; - if (strlen(branches) > 0) info = branches; - else info = ourbranches.Data()+pos+9; - printf("Animating tree, branches=%s\n",info.Data()); + TString info = GetRecentInfo(); + + auto pos = info.Index(", basket="); + if (pos == kNPOS) + return; + info.Remove(pos); + + pos = info.Index(", branch="); + if (pos == kNPOS) + return; + TString select_branches = info(pos + 9, info.Length() - pos - 9); + + auto colon = info.Index("::"); + if (colon == kNPOS) + return; + + info.Resize(colon - 1); + + auto tree = fFile->Get(info); + if (!tree) + return; + if (branches && *branches) + select_branches = branches; // create list of branches Int_t nzip = 0; TBranch *branch; TObjArray list; char *comma; - while((comma = strrchr((char*)info.Data(),','))) { + while((comma = strrchr((char*)select_branches.Data(),','))) { *comma = 0; comma++; while (*comma == ' ') comma++; @@ -177,7 +205,7 @@ void TFileDrawMap::AnimateTree(const char *branches) list.Add(branch); } } - comma = (char*)info.Data(); + comma = (char*)select_branches.Data(); while (*comma == ' ') comma++; branch = tree->GetBranch(comma); if (branch) { @@ -275,34 +303,40 @@ void TFileDrawMap::DrawMarker(Int_t marker, Long64_t eseek) void TFileDrawMap::DrawObject() { TVirtualPad *padsave = gROOT->GetSelectedPad(); - if (padsave == gPad) { + if ((padsave == gPad) || (gPad && gPad->IsWeb())) { //must create a new canvas gROOT->MakeDefCanvas(); } else if (padsave) { padsave->cd(); } + TString info = GetRecentInfo(); + // case of a TTree - char *info = new char[fName.Length()+1]; - strlcpy(info,fName.Data(),fName.Length()+1); - char *cbasket = (char*)strstr(info,", basket="); - if (cbasket) { - *cbasket = 0; - char *cbranch = (char*)strstr(info,", branch="); - if (!cbranch) { delete [] info; return; } - *cbranch = 0; - cbranch += 9; - TTree *tree = (TTree*)fFile->Get(info); - if (tree) tree->Draw(cbranch); - delete [] info; - return; - } + auto pbasket = info.Index(", basket="); + if (pbasket != kNPOS) { + info.Resize(pbasket); + auto pbranch = info.Index(", branch="); + if (pbranch == kNPOS) + return; - delete [] info; + TString cbranch = info(pbranch + 9, info.Length() - pbranch - 9); - // other objects - TObject *obj = GetObject(); - if (obj) obj->Draw(); + auto colon = info.Index("::"); + if (colon == kNPOS) + return; + + info.Resize(colon - 1); + + auto tree = fFile->Get(info); + if (tree) + tree->Draw(cbranch); + } else { + // other objects + auto obj = GetObject(); + if (obj) + obj->Draw(); + } } @@ -316,25 +350,25 @@ void TFileDrawMap::DumpObject() obj->Dump(); return; } - char *centry = (char*)strstr(GetName(),"entry="); - if (!centry) return; + + TString info = GetRecentInfo(); + + auto indx = info.Index("entry="); + if (indx == kNPOS) + return; + Int_t entry = 0; - sscanf(centry+6,"%d",&entry); - TString info(GetName()); - char *colon = (char*)strstr((char*)info.Data(),"::"); - if (!colon) return; - colon--; - *colon = 0; - TTree *tree; fFile->GetObject(info.Data(),tree); - if (tree) tree->Show(entry); -} + sscanf(info.Data() + indx + 6, "%d", &entry); -//////////////////////////////////////////////////////////////////////////////// -/// Execute action corresponding to one event. + auto colon = info.Index("::"); + if (colon == kNPOS) + return; -void TFileDrawMap::ExecuteEvent(Int_t event, Int_t px, Int_t py) -{ - fFrame->ExecuteEvent(event,px,py); + info.Resize(colon - 1); + + auto tree = fFile->Get(info); + if (tree) + tree->Show(entry); } //////////////////////////////////////////////////////////////////////////////// @@ -342,19 +376,18 @@ void TFileDrawMap::ExecuteEvent(Int_t event, Int_t px, Int_t py) TObject *TFileDrawMap::GetObject() { - if (strstr(GetName(),"entry=")) return nullptr; - char *info = new char[fName.Length()+1]; - strlcpy(info,fName.Data(),fName.Length()+1); - char *colon = strstr(info,"::"); - if (!colon) { - delete [] info; + TString info = GetRecentInfo(); + + if (info.Contains("entry=")) return nullptr; - } - colon--; - *colon = 0; - auto res = fFile->Get(info); - delete [] info; - return res; + + auto colon = info.Index("::"); + if (colon == kNPOS) + return nullptr; + + info.Resize(colon - 1); + + return fFile->Get(info); } //////////////////////////////////////////////////////////////////////////////// @@ -408,23 +441,23 @@ bool TFileDrawMap::GetObjectInfoDir(TDirectory *dir, Int_t px, Int_t py, TString if (cl && cl->InheritsFrom(TTree::Class())) { TTree *tree = (TTree*)gDirectory->Get(key->GetName()); TIter nextb(tree->GetListOfLeaves()); - TLeaf *leaf; - while ((leaf = (TLeaf*)nextb())) { + while (auto leaf = (TLeaf *)nextb()) { TBranch *branch = leaf->GetBranch(); Int_t nbaskets = branch->GetMaxBaskets(); Int_t offsets = branch->GetEntryOffsetLen(); Int_t len = leaf->GetLen(); - for (Int_t i=0;iGetBasketSeek(i); - if (!bseek) break; + if (!bseek) + break; nbytes = branch->GetBasketBytes()[i]; - if (pbyte >= bseek && pbyte < bseek+nbytes) { + if (pbyte >= bseek && pbyte < bseek + nbytes) { Int_t entry = branch->GetBasketEntry()[i]; if (!offsets) entry += (pbyte-bseek)/len; if (curdir == (TDirectory*)fFile) { - info.Form("%s%s, branch=%s, basket=%d, entry=%d",curdir->GetPath(),key->GetName(),branch->GetName(),i,entry); + info.Form("%s%s ::%s, branch=%s, basket=%d, entry=%d",curdir->GetPath(),key->GetName(),key->GetClassName(),branch->GetName(),i,entry); } else { - info.Form("%s/%s, branch=%s, basket=%d, entry=%d",curdir->GetPath(),key->GetName(),branch->GetName(),i,entry); + info.Form("%s/%s ::%s, branch=%s, basket=%d, entry=%d",curdir->GetPath(),key->GetName(),key->GetClassName(),branch->GetName(),i,entry); } return true; } From 06523d78f935bdda7624698901223d2de796828d Mon Sep 17 00:00:00 2001 From: Sergey Linev Date: Tue, 10 Dec 2024 13:22:27 +0100 Subject: [PATCH 10/12] [jsroot] dev 10/12/2024 1. Experimental scaling functionality, generating images with more pixels as on display 2. Provide better interactivity with TWebPainting --- js/build/jsroot.js | 306 ++++++++++++++++-------- js/modules/base/BasePainter.mjs | 3 +- js/modules/base/ObjectPainter.mjs | 41 ++-- js/modules/base/base3d.mjs | 45 +++- js/modules/base/makepdf.mjs | 7 +- js/modules/core.mjs | 4 +- js/modules/draw/TWebPaintingPainter.mjs | 60 ++++- js/modules/gpad/RPadPainter.mjs | 7 +- js/modules/gpad/TFramePainter.mjs | 32 ++- js/modules/gpad/TPadPainter.mjs | 88 ++++--- js/modules/gui.mjs | 5 + js/modules/hist/TPavePainter.mjs | 2 +- js/modules/hist/hist3d.mjs | 17 +- 13 files changed, 414 insertions(+), 203 deletions(-) diff --git a/js/build/jsroot.js b/js/build/jsroot.js index 286801e9b43d2..ea3c24b905844 100644 --- a/js/build/jsroot.js +++ b/js/build/jsroot.js @@ -12,7 +12,7 @@ const version_id = 'dev', /** @summary version date * @desc Release date in format day/month/year like '14/04/2022' */ -version_date = '4/12/2024', +version_date = '10/12/2024', /** @summary version id and date * @desc Produced by concatenation of {@link version_id} and {@link version_date} @@ -216,6 +216,8 @@ settings = { CanvasWidth: 1200, /** @summary Default canvas height */ CanvasHeight: 800, + /** @summary Canvas pixel ratio between viewport and display, default 1 */ + CanvasScale: 1, /** @summary Enable or disable tooltips, default on */ Tooltip: !nodejs, /** @summary Time in msec for appearance of tooltips, 0 - no animation */ @@ -8506,7 +8508,8 @@ function getElementRect(elem, sizearg) { /** @summary Calculate absolute position of provided element in canvas * @private */ function getAbsPosInCanvas(sel, pos) { - if (!pos) return pos; + if (!pos) + return pos; while (!sel.empty() && !sel.classed('root_canvas')) { const cl = sel.attr('class'); @@ -13065,12 +13068,12 @@ class ObjectPainter extends BasePainter { /** @summary Fill context menu for the object * @private */ fillContextMenu(menu) { - const name = this.getObjectName(); - let cl = this.getClassName(); - const p = cl.lastIndexOf('::'); - if (p > 0) cl = cl.slice(p+2); - const hdr = (cl && name) ? `${cl}:${name}` : (cl || name || 'object'), - url = (p < 0) ? `${urlClassPrefix}${cl}.html` : ''; + const cl = this.getClassName(), + name = this.getObjectName(), + p = cl.lastIndexOf('::'), + cl0 = (p > 0) ? cl.slice(p+2) : cl, + hdr = (cl0 && name) ? `${cl0}:${name}` : (cl0 || name || 'object'), + url = cl ? `${urlClassPrefix}${cl.replaceAll('::', '_1_1')}.html` : ''; menu.header(hdr, url); @@ -13555,7 +13558,7 @@ class ObjectPainter extends BasePainter { if (!this.snapid || !canvp || canvp?._readonly || !canvp?._websocket) return menu; - function DoExecMenu(arg) { + function doExecMenu(arg) { const execp = menu.exec_painter || this, cp = execp.getCanvPainter(), item = menu.exec_items[parseInt(arg)]; @@ -13569,16 +13572,18 @@ class ObjectPainter extends BasePainter { return; } - if (isFunc(cp?.executeObjectMethod)) - if (cp.executeObjectMethod(execp, item, item.$execid)) return; + if (isFunc(cp?.executeObjectMethod) && cp.executeObjectMethod(execp, item, item.$execid)) + return; item.fClassName = execp.getClassName(); if ((item.$execid.indexOf('#x') > 0) || (item.$execid.indexOf('#y') > 0) || (item.$execid.indexOf('#z') > 0)) item.fClassName = clTAxis; - if (execp.executeMenuCommand(item)) return; + if (execp.executeMenuCommand(item)) + return; - if (!item.$execid) return; + if (!item.$execid) + return; if (!item.fArgs) { if (cp?.v7canvas) @@ -13599,9 +13604,10 @@ class ObjectPainter extends BasePainter { }); } - const DoFillMenu = (_menu, _reqid, _resolveFunc, reply) => { + const doFillMenu = (_menu, _reqid, _resolveFunc, reply) => { // avoid multiple call of the callback after timeout - if (menu._got_menu) return; + if (menu._got_menu) + return; menu._got_menu = true; if (reply && (_reqid !== reply.fId)) @@ -13633,16 +13639,18 @@ class ObjectPainter extends BasePainter { } if ((item.fChecked === undefined) || (item.fChecked < 0)) - _menu.add(item.fName, n, DoExecMenu); + _menu.add(item.fName, n, doExecMenu); else - _menu.addchk(item.fChecked, item.fName, n, DoExecMenu); + _menu.addchk(item.fChecked, item.fName, n, doExecMenu); } - if (lastclname) _menu.endsub(); + if (lastclname) + _menu.endsub(); } _resolveFunc(_menu); }, + reqid = this.getSnapId(kind); menu._got_menu = false; @@ -13661,9 +13669,9 @@ class ObjectPainter extends BasePainter { } // set timeout to avoid menu hanging - setTimeout(() => DoFillMenu(menu, reqid, handleResolve), 2000); + setTimeout(() => doFillMenu(menu, reqid, handleResolve), 2000); - canvp.submitMenuRequest(this, kind, reqid).then(lst => DoFillMenu(menu, reqid, handleResolve, lst)); + canvp.submitMenuRequest(this, kind, reqid).then(lst => doFillMenu(menu, reqid, handleResolve, lst)); }); } @@ -56677,11 +56685,13 @@ const Handling3DDrawings = { * @private */ access3dKind(new_value) { const svg = this.getPadSvg(); - if (svg.empty()) return -1; + if (svg.empty()) + return -1; // returns kind of currently created 3d canvas const kind = svg.property('can3d'); - if (new_value !== undefined) svg.property('can3d', new_value); + if (new_value !== undefined) + svg.property('can3d', new_value); return ((kind === null) || (kind === undefined)) ? -1 : kind; }, @@ -56700,7 +56710,7 @@ const Handling3DDrawings = { else if (browser.isFirefox) can3d = constants$1.Embed3D.Embed; else if (browser.chromeVersion > 95) - // version 96 works partially, 97 works fine + // version 96 works partially, 97 works fine can3d = constants$1.Embed3D.Embed; else can3d = constants$1.Embed3D.Overlay; @@ -56747,14 +56757,22 @@ const Handling3DDrawings = { // while 3D canvas uses area also for the axis labels, extend area relative to normal frame const dx = Math.round(size.width*0.07), dy = Math.round(size.height*0.05); - size.x = Math.max(0, size.x-dx); - size.y = Math.max(0, size.y-dy); + size.x = Math.max(0, size.x - dx); + size.y = Math.max(0, size.y - dy); size.width = Math.min(size.width + 2*dx, rect.width - size.x); size.height = Math.min(size.height + 2*dy, rect.height - size.y); } - if (can3d === 1) + if (can3d === constants$1.Embed3D.Overlay) { size = getAbsPosInCanvas(this.getPadSvg(), size); + const scale = this.getCanvPainter().getPadScale(); + if (scale && scale !== 1) { + size.x /= scale; + size.y /= scale; + size.width /= scale; + size.height /= scale; + } + } return size; }, @@ -57097,6 +57115,7 @@ class TooltipFor3D { this.parent = prnt || getDocument().body; this.canvas = canvas; // we need canvas to recalculate mouse events this.abspos = !prnt; + this.scale = 1; } /** @summary check parent */ @@ -57107,10 +57126,16 @@ class TooltipFor3D { } } + /** @summary set scaling factor */ + setScale(v) { + this.scale = v; + } + /** @summary extract position from event * @desc can be used to process it later when event is gone */ extract_pos(e) { - if (isObject(e) && (e.u !== undefined) && (e.l !== undefined)) return e; + if (isObject(e) && (e.u !== undefined) && (e.l !== undefined)) + return e; const res = { u: 0, l: 0 }; if (this.abspos) { res.l = e.pageX; @@ -57119,6 +57144,8 @@ class TooltipFor3D { res.l = e.offsetX; res.u = e.offsetY; } + res.l /= this.scale; + res.u /= this.scale; return res; } @@ -57126,7 +57153,8 @@ class TooltipFor3D { * @desc event is delivered from canvas, * but position should be calculated relative to the element where tooltip is placed */ pos(e) { - if (!this.tt) return; + if (!this.tt) + return; const pos = this.extract_pos(e); if (!this.abspos) { @@ -57168,10 +57196,12 @@ class TooltipFor3D { /** @summary Show tooltip */ show(v /* , mouse_pos, status_func */) { - if (!v) return this.hide(); + if (!v) + return this.hide(); if (isObject(v) && (v.lines || v.line)) { - if (v.only_status) return this.hide(); + if (v.only_status) + return this.hide(); if (v.line) v = v.line; @@ -57396,7 +57426,7 @@ function createOrbitControl(painter, camera, scene, renderer, lookat) { delete this.mouse_zoom_mesh; }; - control.HideTooltip = function() { + control.hideTooltip = function() { this.tooltip.hide(); }; @@ -57615,7 +57645,6 @@ function createOrbitControl(painter, camera, scene, renderer, lookat) { this.cursor_changed = false; if (tip && this.painter?.isTooltipAllowed()) { this.tooltip.checkParent(this.painter.selectDom().node()); - this.tooltip.show(tip, mouse); this.tooltip.pos(this.tmout_ttpos); } else { @@ -64303,11 +64332,13 @@ const TooltipHandler = { } } - let nhints = 0, nexact = 0, maxlen = 0, lastcolor1 = 0, usecolor1 = false, textheight = 11; + let nhints = 0, nexact = 0, maxlen = 0, lastcolor1 = 0, usecolor1 = false; const hmargin = 3, wmargin = 3, hstep = 1.2, frame_rect = this.getFrameRect(), pp = this.getPadPainter(), pad_width = pp?.getPadWidth(), + scale = this.getCanvPainter()?.getPadScale() ?? 1, + textheight = (pnt?.touch ? 15 : 11) * scale, font = new FontHandler(160, textheight), disable_tootlips = !this.isTooltipAllowed() || !this.tooltip_enabled; @@ -64319,10 +64350,8 @@ const TooltipHandler = { // collect tooltips from pad painter - it has list of all drawn objects const hints = pp?.processPadTooltipEvent(pnt) ?? []; - if (pp?._deliver_webcanvas_events && pp?.is_active_pad && pnt && isFunc(pp?.deliverWebCanvasEvent)) - pp.deliverWebCanvasEvent('move', frame_rect.x + pnt.x, frame_rect.y + pnt.y, hints); - - if (pnt?.touch) textheight = 15; + if (pnt && frame_rect) + pp.deliverWebCanvasEvent('move', frame_rect.x + pnt.x, frame_rect.y + pnt.y, hints ? hints[0]?.painter?.snapid : ''); for (let n = 0; n < hints.length; ++n) { const hint = hints[n]; @@ -64356,7 +64385,8 @@ const TooltipHandler = { hint.height = Math.round(hint.lines.length * textheight * hstep + 2 * hmargin - textheight * (hstep - 1)); if ((hint.color1 !== undefined) && (hint.color1 !== 'none')) { - if ((lastcolor1 !== 0) && (lastcolor1 !== hint.color1)) usecolor1 = true; + if ((lastcolor1 !== 0) && (lastcolor1 !== hint.color1)) + usecolor1 = true; lastcolor1 = hint.color1; } } @@ -64387,21 +64417,27 @@ const TooltipHandler = { if (!hint) hint = hints[k]; // select exact hint if this is the only one - if (hints[k].exact && (nexact < 2) && (!hint || !hint.exact)) { hint = hints[k]; break; } + if (hints[k].exact && (nexact < 2) && (!hint || !hint.exact)) { + hint = hints[k]; + break; + } - if (!pnt || (hints[k].x === undefined) || (hints[k].y === undefined)) continue; + if (!pnt || (hints[k].x === undefined) || (hints[k].y === undefined)) + continue; const dist2 = (pnt.x - hints[k].x) ** 2 + (pnt.y - hints[k].y) ** 2; if (dist2 < best_dist2) { best_dist2 = dist2; best_hint = hints[k]; } } - if ((!hint || !hint.exact) && (best_dist2 < 400)) hint = best_hint; + if ((!hint || !hint.exact) && (best_dist2 < 400)) + hint = best_hint; if (hint) { name = (hint.lines && hint.lines.length > 1) ? hint.lines[0] : hint.name; title = hint.title || ''; info = hint.line; - if (!info && hint.lines) info = hint.lines.slice(1).join(' '); + if (!info && hint.lines) + info = hint.lines.slice(1).join(' '); } this.showObjectStatus(name, title, info, coordinates); @@ -64433,7 +64469,7 @@ const TooltipHandler = { .property('hints_pad', this.getPadName()); let viewmode = hintsg.property('viewmode') || '', - actualw = 0, posx = pnt.x + frame_rect.hint_delta_x; + actualw = 0, posx = pnt.x + frame_rect.hint_delta_x; if (show_only_best || (nhints === 1)) { viewmode = 'single'; @@ -64503,7 +64539,8 @@ const TooltipHandler = { n = -1; } } - if ((gapminx === -1111) && (gapmaxx === -1111)) gapminx = gapmaxx = hint.x; + if ((gapminx === -1111) && (gapmaxx === -1111)) + gapminx = gapmaxx = hint.x; gapminx = Math.min(gapminx, hint.x); gapmaxx = Math.min(gapmaxx, hint.x); } @@ -68948,7 +68985,7 @@ const PadButtonsHandler = { item.tooltip + (iscan ? '' : (` on pad ${this.this_pad_name}`)) + (item.keyname ? ` (keyshortcut ${item.keyname})` : ''), false); if (group.property('vertical')) - svg.attr('x', y).attr('y', x); + svg.attr('x', y).attr('y', x); else svg.attr('x', x).attr('y', y); @@ -69122,6 +69159,9 @@ class TPadPainter extends ObjectPainter { /** @summary get pad height */ getPadHeight() { return this._pad_height || 0; } + /** @summary get pad height */ + getPadScale() { return this._pad_scale || 1; } + /** @summary get pad rect */ getPadRect() { return { @@ -69517,35 +69557,36 @@ class TPadPainter extends ObjectPainter { svg.style('filter', settings.DarkMode || this.pad?.$dark ? 'invert(100%)' : null); - svg.attr('viewBox', `0 0 ${rect.width} ${rect.height}`) - .attr('preserveAspectRatio', 'none') // we do not preserve relative ratio - .property('height_factor', factor) - .property('draw_x', 0) - .property('draw_y', 0) - .property('draw_width', rect.width) - .property('draw_height', rect.height); - + this._pad_scale = settings.CanvasScale || 1; this._pad_x = 0; this._pad_y = 0; - this._pad_width = rect.width; - this._pad_height = rect.height; + this._pad_width = rect.width * this._pad_scale; + this._pad_height = rect.height * this._pad_scale; + + svg.attr('viewBox', `0 0 ${this._pad_width} ${this._pad_height}`) + .attr('preserveAspectRatio', 'none') // we do not preserve relative ratio + .property('height_factor', factor) + .property('draw_x', this._pad_x) + .property('draw_y', this._pad_y) + .property('draw_width', this._pad_width) + .property('draw_height', this._pad_height); this.addPadBorder(svg, frect); - this.setFastDrawing(rect.width * (1 - this.pad.fLeftMargin - this.pad.fRightMargin), rect.height * (1 - this.pad.fBottomMargin - this.pad.fTopMargin)); + this.setFastDrawing(this._pad_width * (1 - this.pad.fLeftMargin - this.pad.fRightMargin), this._pad_height * (1 - this.pad.fBottomMargin - this.pad.fTopMargin)); if (this.alignButtons && btns) - this.alignButtons(btns, rect.width, rect.height); + this.alignButtons(btns, this._pad_width, this._pad_height); let dt = info.selectChild('.canvas_date'); if (!gStyle.fOptDate) dt.remove(); else { if (dt.empty()) - dt = info.append('text').attr('class', 'canvas_date'); - const posy = Math.round(rect.height * (1 - gStyle.fDateY)), + dt = info.append('text').attr('class', 'canvas_date'); + const posy = Math.round(this._pad_height * (1 - gStyle.fDateY)), date = new Date(); - let posx = Math.round(rect.width * gStyle.fDateX); + let posx = Math.round(this._pad_width * gStyle.fDateX); if (!is_batch && (posx < 25)) posx = 25; if (gStyle.fOptDate > 3) @@ -69702,6 +69743,7 @@ class TPadPainter extends ObjectPainter { .property('draw_width', w) .property('draw_height', h); + this._pad_scale = 1; // subpads always use scale 1 while placed inside canvas viewBox this._pad_x = x; this._pad_y = y; this._pad_width = w; @@ -69718,7 +69760,7 @@ class TPadPainter extends ObjectPainter { } if (this.alignButtons && btns) - this.alignButtons(btns, w, h); + this.alignButtons(btns, this._pad_width, this._pad_height); return pad_visible; } @@ -70718,7 +70760,7 @@ class TPadPainter extends ObjectPainter { first.fPrimitives = null; // primitives are not interesting, they are disabled in IO // if there are execs in the pad, deliver events to the server - this._deliver_webcanvas_events = first.fExecs?.arr?.length > 0; + this._deliver_move_events = first.fExecs?.arr?.length > 0; if (this.snapid === undefined) { // first time getting snap, create all gui elements first @@ -70855,7 +70897,8 @@ class TPadPainter extends ObjectPainter { fp.cleanFrameDrawings(); fp.redraw(); } - if (isFunc(this.removePadButtons)) this.removePadButtons(); + if (isFunc(this.removePadButtons)) + this.removePadButtons(); this.addPadButtons(true); } @@ -70871,16 +70914,16 @@ class TPadPainter extends ObjectPainter { /** @summary Deliver mouse move or click event to the web canvas * @private */ - deliverWebCanvasEvent(kind, x, y, hints) { - if (!this._deliver_webcanvas_events || !this.is_active_pad || this.doingDraw() || x === undefined || y === undefined) return; + deliverWebCanvasEvent(kind, x, y, snapid) { + if (!this.is_active_pad || this.doingDraw() || x === undefined || y === undefined) + return; + if ((kind === 'move') && !this._deliver_move_events) + return; const cp = this.getCanvPainter(); - if (!cp || !cp._websocket || !cp._websocket.canSend(2) || cp._readonly) return; - - let selobj_snapid = ''; - if (hints && hints[0] && hints[0].painter?.snapid) - selobj_snapid = hints[0].painter.snapid.toString(); + if (!cp || !cp._websocket || !cp._websocket.canSend(2) || cp._readonly) + return; - const msg = JSON.stringify([this.snapid, kind, x.toString(), y.toString(), selobj_snapid]); + const msg = JSON.stringify([this.snapid, kind, x.toString(), y.toString(), snapid ? snapid.toString() : '']); cp.sendWebsocket(`EVENT:${msg}`); } @@ -71197,16 +71240,24 @@ class TPadPainter extends ObjectPainter { .attr('href', dataUrl); }, 'pads'); - let width = elem.property('draw_width'), height = elem.property('draw_height'); + let width = elem.property('draw_width'), + height = elem.property('draw_height'), + viewBox = ''; if (use_frame) { const fp = this.getFramePainter(); width = fp.getFrameWidth(); height = fp.getFrameHeight(); } + const scale = this.getCanvPainter()?.getPadScale() ?? 1; + if (scale !== 1) { + viewBox = `viewBox="0 0 ${width} ${height}"`; + width = Math.round(width / scale); + height = Math.round(height / scale); + } const arg = (file_format === 'pdf') - ? { node: elem.node(), width, height, reset_tranform: use_frame } - : compressSVG(`${elem.node().innerHTML}`); + ? { node: elem.node(), width, height, scale, reset_tranform: use_frame } + : compressSVG(`${elem.node().innerHTML}`); return svgToImage(arg, file_format, args).then(res => { // reactivate border @@ -71276,10 +71327,16 @@ class TPadPainter extends ObjectPainter { const shown = []; this.painters.forEach((pp, indx) => { const obj = pp?.getObject(); - if (!obj || (shown.indexOf(obj) >= 0)) return; - let name = isFunc(pp.getClassName) ? pp.getClassName() : (obj._typename || ''); - if (name) name += '::'; - name += isFunc(pp.getObjectName) ? pp.getObjectName() : (obj.fName || `item${indx}`); + if (!obj || (shown.indexOf(obj) >= 0)) + return; + let name = ''; + if (isFunc(pp.getMenuHeader)) + name = pp.getMenuHeader(); + else { + name = isFunc(pp.getClassName) ? pp.getClassName() : (obj._typename || ''); + if (name) name += '::'; + name += isFunc(pp.getObjectName) ? pp.getObjectName() : (obj.fName || `item${indx}`); + } menu.add(name, indx, this.itemContextMenu); shown.push(obj); }); @@ -73045,7 +73102,7 @@ class TPavePainter extends ObjectPainter { arg.y = texty + 0.05 * stepy; arg.height = 0.9*stepy; // prevent expand of normal title on full width - // if (this.isTitle() && (halign === 2) && (arg.width > 0.1*pad_width) && (arg.width < 0.7*pad_width)) { + // if (this.isTitle() && (halign === 2) && (arg.width > 0.1*pad_width) && (arg.width < 0.7*pad_width)) { // arg.width -= 0.02*pad_width; // arg.x = 0.01*pad_width; // } @@ -80453,6 +80510,13 @@ function create3DControl(fp) { const frame_painter = fp, obj_painter = fp.getMainPainter(); + if (fp.access3dKind() === constants$1.Embed3D.Embed) { + // tooltip scaling only need when GL canvas embed into + const scale = fp.getCanvPainter()?.getPadScale(); + if (scale) + fp.control.tooltip?.setScale(scale); + } + fp.control.processMouseMove = function(intersects) { let tip = null, mesh = null, zoom_mesh = null; const handle_tooltip = frame_painter.isTooltipAllowed(); @@ -80567,7 +80631,7 @@ function create3DScene(render3d, x3dscale, y3dscale, orthographic) { disposeThreejsObject(this.toplevel); delete this.tooltip_mesh; delete this.toplevel; - if (this.control) this.control.HideTooltip(); + this.control?.hideTooltip(); const newtop = new THREE.Object3D(); this.scene.add(newtop); @@ -80614,7 +80678,7 @@ function create3DScene(render3d, x3dscale, y3dscale, orthographic) { }).then(r => { this.renderer = r; - this.webgl = (r.jsroot_render3d === constants$1.Render3D.WebGL); + this.webgl = r.jsroot_render3d === constants$1.Render3D.WebGL; this.add3dCanvas(sz, r.jsroot_dom, this.webgl); this.first_render_tm = 0; @@ -80752,9 +80816,11 @@ function resize3D() { this.apply3dSize(sz); - if ((this.scene_width === sz.width) && (this.scene_height === sz.height)) return false; + if ((this.scene_width === sz.width) && (this.scene_height === sz.height)) + return false; - if ((sz.width < 10) || (sz.height < 10)) return false; + if ((sz.width < 10) || (sz.height < 10)) + return false; this.scene_width = sz.width; this.scene_height = sz.height; @@ -142621,13 +142687,9 @@ async function makePDF(svg, args) { }; } - - let doc; - const orientation = (svg.width < svg.height) ? 'portrait' : 'landscape'; - if (args?.as_doc) - doc = args?.doc; + let doc = args?.as_doc ? args.doc : null; if (doc) { doc.addPage({ @@ -147500,6 +147562,11 @@ function readStyleFromURL(url) { } } + if (d.has('scale')) { + const s = parseInt(d.get('scale')); + settings.CanvasScale = Number.isInteger(s) ? s : 2; + } + const b = d.get('batch'); if (b !== undefined) { setBatchMode(d !== 'off'); @@ -155032,16 +155099,44 @@ class TWebPaintingPainter extends ObjectPainter { /** @summary Update TWebPainting object */ updateObject(obj) { - if (!this.matchObjectType(obj)) return false; + if (!this.matchObjectType(obj)) + return false; this.assignObject(obj); return true; } + /** @summary Provides menu header */ + getMenuHeader() { + return this.getObject()?.fClassName || 'TWebPainting'; + } + + /** @summary Fill context menu + * @desc Create only header, items will be requested from server */ + fillContextMenu(menu) { + const cl = this.getMenuHeader(); + menu.header(cl, `${urlClassPrefix}${cl}.html`); + return true; + } + + /** @summary Mouse click handler + * @desc Redirect mouse click events to the ROOT application + * @private */ + handleMouseClick(evnt) { + const pos = pointer(evnt, this.draw_g.node()), + pp = this.getPadPainter(), + rect = pp?.getPadRect(); + + if (pp && rect && this.snapid) + pp.selectObjectPainter(this, { x: pos[0] + rect.x, y: pos[1] + rect.y }); + // pp.deliverWebCanvasEvent('click', pos[0] + rect.x, pos[1] + rect.y, this.snapid); + } + /** @summary draw TWebPainting object */ async redraw() { const obj = this.getObject(), func = this.getAxisToSvgFunc(); - if (!obj?.fOper || !func) return; + if (!obj?.fOper || !func) + return this; let indx = 0, attr = {}, lastpath = null, lastkind = 'none', d = '', oper, npoints, n; @@ -155049,15 +155144,17 @@ class TWebPaintingPainter extends ObjectPainter { const arr = obj.fOper.split(';'), check_attributes = kind => { if (kind === lastkind) - return this; + return; if (lastpath) { lastpath.attr('d', d); // flush previous - d = ''; lastpath = null; lastkind = 'none'; + d = ''; + lastpath = null; + lastkind = 'none'; } if (!kind) - return this; + return; lastkind = kind; lastpath = this.draw_g.append('svg:path').attr('d', ''); // placeholder for 'd' to have it always in front @@ -155066,7 +155163,6 @@ class TWebPaintingPainter extends ObjectPainter { case 'l': lastpath.call(this.lineatt.func).style('fill', 'none'); break; case 'm': lastpath.call(this.markeratt.func); break; } - return this; }, read_attr = (str, names) => { let lastp = 0; const obj = { _typename: 'any' }; @@ -155102,12 +155198,11 @@ class TWebPaintingPainter extends ObjectPainter { check_attributes((oper === 'b') ? 'f' : 'l'); const x1 = func.x(obj.fBuf[indx++]), - y1 = func.y(obj.fBuf[indx++]), - x2 = func.x(obj.fBuf[indx++]), - y2 = func.y(obj.fBuf[indx++]); + y1 = func.y(obj.fBuf[indx++]), + x2 = func.x(obj.fBuf[indx++]), + y2 = func.y(obj.fBuf[indx++]); d += `M${x1},${y1}h${x2-x1}v${y2-y1}h${x1-x2}z`; - continue; } case 'l': @@ -155182,7 +155277,13 @@ class TWebPaintingPainter extends ObjectPainter { this.createG(); - return process(-1).then(() => check_attributes()); + return process(-1).then(() => { + check_attributes(); + assignContextMenu(this); + if (!this.isBatchMode()) + this.draw_g.on('click', evnt => this.handleMouseClick(evnt)); + return this; + }); } static async draw(dom, obj) { @@ -160540,8 +160641,11 @@ class RPadPainter extends RObjectPainter { /** @summary Redraw legend object * @desc Used when object attributes are changed to ensure that legend is up to date * @private */ - async redrawLegend() { - } + async redrawLegend() {} + + /** @summary Deliver mouse move or click event to the web canvas + * @private */ + deliverWebCanvasEvent() {} /** @summary Redraw pad means redraw ourself * @return {Promise} when redrawing ready */ diff --git a/js/modules/base/BasePainter.mjs b/js/modules/base/BasePainter.mjs index 5a54a98e3095b..7fb721c2f7e3f 100644 --- a/js/modules/base/BasePainter.mjs +++ b/js/modules/base/BasePainter.mjs @@ -58,7 +58,8 @@ function getElementRect(elem, sizearg) { /** @summary Calculate absolute position of provided element in canvas * @private */ function getAbsPosInCanvas(sel, pos) { - if (!pos) return pos; + if (!pos) + return pos; while (!sel.empty() && !sel.classed('root_canvas')) { const cl = sel.attr('class'); diff --git a/js/modules/base/ObjectPainter.mjs b/js/modules/base/ObjectPainter.mjs index d3219ac012f33..4f767a273ade4 100644 --- a/js/modules/base/ObjectPainter.mjs +++ b/js/modules/base/ObjectPainter.mjs @@ -888,12 +888,12 @@ class ObjectPainter extends BasePainter { /** @summary Fill context menu for the object * @private */ fillContextMenu(menu) { - const name = this.getObjectName(); - let cl = this.getClassName(); - const p = cl.lastIndexOf('::'); - if (p > 0) cl = cl.slice(p+2); - const hdr = (cl && name) ? `${cl}:${name}` : (cl || name || 'object'), - url = (p < 0) ? `${urlClassPrefix}${cl}.html` : ''; + const cl = this.getClassName(), + name = this.getObjectName(), + p = cl.lastIndexOf('::'), + cl0 = (p > 0) ? cl.slice(p+2) : cl, + hdr = (cl0 && name) ? `${cl0}:${name}` : (cl0 || name || 'object'), + url = cl ? `${urlClassPrefix}${cl.replaceAll('::', '_1_1')}.html` : ''; menu.header(hdr, url); @@ -1378,7 +1378,7 @@ class ObjectPainter extends BasePainter { if (!this.snapid || !canvp || canvp?._readonly || !canvp?._websocket) return menu; - function DoExecMenu(arg) { + function doExecMenu(arg) { const execp = menu.exec_painter || this, cp = execp.getCanvPainter(), item = menu.exec_items[parseInt(arg)]; @@ -1392,16 +1392,18 @@ class ObjectPainter extends BasePainter { return; } - if (isFunc(cp?.executeObjectMethod)) - if (cp.executeObjectMethod(execp, item, item.$execid)) return; + if (isFunc(cp?.executeObjectMethod) && cp.executeObjectMethod(execp, item, item.$execid)) + return; item.fClassName = execp.getClassName(); if ((item.$execid.indexOf('#x') > 0) || (item.$execid.indexOf('#y') > 0) || (item.$execid.indexOf('#z') > 0)) item.fClassName = clTAxis; - if (execp.executeMenuCommand(item)) return; + if (execp.executeMenuCommand(item)) + return; - if (!item.$execid) return; + if (!item.$execid) + return; if (!item.fArgs) { if (cp?.v7canvas) @@ -1422,9 +1424,10 @@ class ObjectPainter extends BasePainter { }); } - const DoFillMenu = (_menu, _reqid, _resolveFunc, reply) => { + const doFillMenu = (_menu, _reqid, _resolveFunc, reply) => { // avoid multiple call of the callback after timeout - if (menu._got_menu) return; + if (menu._got_menu) + return; menu._got_menu = true; if (reply && (_reqid !== reply.fId)) @@ -1456,16 +1459,18 @@ class ObjectPainter extends BasePainter { } if ((item.fChecked === undefined) || (item.fChecked < 0)) - _menu.add(item.fName, n, DoExecMenu); + _menu.add(item.fName, n, doExecMenu); else - _menu.addchk(item.fChecked, item.fName, n, DoExecMenu); + _menu.addchk(item.fChecked, item.fName, n, doExecMenu); } - if (lastclname) _menu.endsub(); + if (lastclname) + _menu.endsub(); } _resolveFunc(_menu); }, + reqid = this.getSnapId(kind); menu._got_menu = false; @@ -1484,9 +1489,9 @@ class ObjectPainter extends BasePainter { } // set timeout to avoid menu hanging - setTimeout(() => DoFillMenu(menu, reqid, handleResolve), 2000); + setTimeout(() => doFillMenu(menu, reqid, handleResolve), 2000); - canvp.submitMenuRequest(this, kind, reqid).then(lst => DoFillMenu(menu, reqid, handleResolve, lst)); + canvp.submitMenuRequest(this, kind, reqid).then(lst => doFillMenu(menu, reqid, handleResolve, lst)); }); } diff --git a/js/modules/base/base3d.mjs b/js/modules/base/base3d.mjs index d6f3449dbb198..109fb84235529 100644 --- a/js/modules/base/base3d.mjs +++ b/js/modules/base/base3d.mjs @@ -246,11 +246,13 @@ const Handling3DDrawings = { * @private */ access3dKind(new_value) { const svg = this.getPadSvg(); - if (svg.empty()) return -1; + if (svg.empty()) + return -1; // returns kind of currently created 3d canvas const kind = svg.property('can3d'); - if (new_value !== undefined) svg.property('can3d', new_value); + if (new_value !== undefined) + svg.property('can3d', new_value); return ((kind === null) || (kind === undefined)) ? -1 : kind; }, @@ -269,7 +271,7 @@ const Handling3DDrawings = { else if (browser.isFirefox) can3d = constants.Embed3D.Embed; else if (browser.chromeVersion > 95) - // version 96 works partially, 97 works fine + // version 96 works partially, 97 works fine can3d = constants.Embed3D.Embed; else can3d = constants.Embed3D.Overlay; @@ -316,14 +318,22 @@ const Handling3DDrawings = { // while 3D canvas uses area also for the axis labels, extend area relative to normal frame const dx = Math.round(size.width*0.07), dy = Math.round(size.height*0.05); - size.x = Math.max(0, size.x-dx); - size.y = Math.max(0, size.y-dy); + size.x = Math.max(0, size.x - dx); + size.y = Math.max(0, size.y - dy); size.width = Math.min(size.width + 2*dx, rect.width - size.x); size.height = Math.min(size.height + 2*dy, rect.height - size.y); } - if (can3d === 1) + if (can3d === constants.Embed3D.Overlay) { size = getAbsPosInCanvas(this.getPadSvg(), size); + const scale = this.getCanvPainter().getPadScale(); + if (scale && scale !== 1) { + size.x /= scale; + size.y /= scale; + size.width /= scale; + size.height /= scale; + } + } return size; }, @@ -666,6 +676,7 @@ class TooltipFor3D { this.parent = prnt || getDocument().body; this.canvas = canvas; // we need canvas to recalculate mouse events this.abspos = !prnt; + this.scale = 1; } /** @summary check parent */ @@ -676,10 +687,16 @@ class TooltipFor3D { } } + /** @summary set scaling factor */ + setScale(v) { + this.scale = v; + } + /** @summary extract position from event * @desc can be used to process it later when event is gone */ extract_pos(e) { - if (isObject(e) && (e.u !== undefined) && (e.l !== undefined)) return e; + if (isObject(e) && (e.u !== undefined) && (e.l !== undefined)) + return e; const res = { u: 0, l: 0 }; if (this.abspos) { res.l = e.pageX; @@ -688,6 +705,8 @@ class TooltipFor3D { res.l = e.offsetX; res.u = e.offsetY; } + res.l /= this.scale; + res.u /= this.scale; return res; } @@ -695,7 +714,8 @@ class TooltipFor3D { * @desc event is delivered from canvas, * but position should be calculated relative to the element where tooltip is placed */ pos(e) { - if (!this.tt) return; + if (!this.tt) + return; const pos = this.extract_pos(e); if (!this.abspos) { @@ -737,10 +757,12 @@ class TooltipFor3D { /** @summary Show tooltip */ show(v /* , mouse_pos, status_func */) { - if (!v) return this.hide(); + if (!v) + return this.hide(); if (isObject(v) && (v.lines || v.line)) { - if (v.only_status) return this.hide(); + if (v.only_status) + return this.hide(); if (v.line) v = v.line; @@ -965,7 +987,7 @@ function createOrbitControl(painter, camera, scene, renderer, lookat) { delete this.mouse_zoom_mesh; }; - control.HideTooltip = function() { + control.hideTooltip = function() { this.tooltip.hide(); }; @@ -1184,7 +1206,6 @@ function createOrbitControl(painter, camera, scene, renderer, lookat) { this.cursor_changed = false; if (tip && this.painter?.isTooltipAllowed()) { this.tooltip.checkParent(this.painter.selectDom().node()); - this.tooltip.show(tip, mouse); this.tooltip.pos(this.tmout_ttpos); } else { diff --git a/js/modules/base/makepdf.mjs b/js/modules/base/makepdf.mjs index d7589ed60da29..9bb15c0f679e1 100644 --- a/js/modules/base/makepdf.mjs +++ b/js/modules/base/makepdf.mjs @@ -82,13 +82,9 @@ async function makePDF(svg, args) { }; } - - let doc; - const orientation = (svg.width < svg.height) ? 'portrait' : 'landscape'; - if (args?.as_doc) - doc = args?.doc; + let doc = args?.as_doc ? args.doc : null; if (doc) { doc.addPage({ @@ -164,5 +160,4 @@ async function makePDF(svg, args) { }); } - export { makePDF }; diff --git a/js/modules/core.mjs b/js/modules/core.mjs index 68d8f4bb0cd09..6fc8cdb4e9ec8 100644 --- a/js/modules/core.mjs +++ b/js/modules/core.mjs @@ -4,7 +4,7 @@ const version_id = 'dev', /** @summary version date * @desc Release date in format day/month/year like '14/04/2022' */ -version_date = '4/12/2024', +version_date = '10/12/2024', /** @summary version id and date * @desc Produced by concatenation of {@link version_id} and {@link version_date} @@ -208,6 +208,8 @@ settings = { CanvasWidth: 1200, /** @summary Default canvas height */ CanvasHeight: 800, + /** @summary Canvas pixel ratio between viewport and display, default 1 */ + CanvasScale: 1, /** @summary Enable or disable tooltips, default on */ Tooltip: !nodejs, /** @summary Time in msec for appearance of tooltips, 0 - no animation */ diff --git a/js/modules/draw/TWebPaintingPainter.mjs b/js/modules/draw/TWebPaintingPainter.mjs index 372aadb34272f..9be34c5a991e6 100644 --- a/js/modules/draw/TWebPaintingPainter.mjs +++ b/js/modules/draw/TWebPaintingPainter.mjs @@ -1,5 +1,8 @@ import { getColor } from '../base/colors.mjs'; import { ObjectPainter } from '../base/ObjectPainter.mjs'; +import { pointer as d3_pointer } from '../d3.mjs'; +import { urlClassPrefix } from '../core.mjs'; +import { assignContextMenu } from '../gui/menu.mjs'; /** @summary Draw direct TVirtualX commands into SVG @@ -9,16 +12,44 @@ class TWebPaintingPainter extends ObjectPainter { /** @summary Update TWebPainting object */ updateObject(obj) { - if (!this.matchObjectType(obj)) return false; + if (!this.matchObjectType(obj)) + return false; this.assignObject(obj); return true; } + /** @summary Provides menu header */ + getMenuHeader() { + return this.getObject()?.fClassName || 'TWebPainting'; + } + + /** @summary Fill context menu + * @desc Create only header, items will be requested from server */ + fillContextMenu(menu) { + const cl = this.getMenuHeader(); + menu.header(cl, `${urlClassPrefix}${cl}.html`); + return true; + } + + /** @summary Mouse click handler + * @desc Redirect mouse click events to the ROOT application + * @private */ + handleMouseClick(evnt) { + const pos = d3_pointer(evnt, this.draw_g.node()), + pp = this.getPadPainter(), + rect = pp?.getPadRect(); + + if (pp && rect && this.snapid) + pp.selectObjectPainter(this, { x: pos[0] + rect.x, y: pos[1] + rect.y }); + // pp.deliverWebCanvasEvent('click', pos[0] + rect.x, pos[1] + rect.y, this.snapid); + } + /** @summary draw TWebPainting object */ async redraw() { const obj = this.getObject(), func = this.getAxisToSvgFunc(); - if (!obj?.fOper || !func) return; + if (!obj?.fOper || !func) + return this; let indx = 0, attr = {}, lastpath = null, lastkind = 'none', d = '', oper, npoints, n; @@ -26,15 +57,17 @@ class TWebPaintingPainter extends ObjectPainter { const arr = obj.fOper.split(';'), check_attributes = kind => { if (kind === lastkind) - return this; + return; if (lastpath) { lastpath.attr('d', d); // flush previous - d = ''; lastpath = null; lastkind = 'none'; + d = ''; + lastpath = null; + lastkind = 'none'; } if (!kind) - return this; + return; lastkind = kind; lastpath = this.draw_g.append('svg:path').attr('d', ''); // placeholder for 'd' to have it always in front @@ -43,7 +76,6 @@ class TWebPaintingPainter extends ObjectPainter { case 'l': lastpath.call(this.lineatt.func).style('fill', 'none'); break; case 'm': lastpath.call(this.markeratt.func); break; } - return this; }, read_attr = (str, names) => { let lastp = 0; const obj = { _typename: 'any' }; @@ -79,12 +111,11 @@ class TWebPaintingPainter extends ObjectPainter { check_attributes((oper === 'b') ? 'f' : 'l'); const x1 = func.x(obj.fBuf[indx++]), - y1 = func.y(obj.fBuf[indx++]), - x2 = func.x(obj.fBuf[indx++]), - y2 = func.y(obj.fBuf[indx++]); + y1 = func.y(obj.fBuf[indx++]), + x2 = func.x(obj.fBuf[indx++]), + y2 = func.y(obj.fBuf[indx++]); d += `M${x1},${y1}h${x2-x1}v${y2-y1}h${x1-x2}z`; - continue; } case 'l': @@ -159,7 +190,13 @@ class TWebPaintingPainter extends ObjectPainter { this.createG(); - return process(-1).then(() => check_attributes()); + return process(-1).then(() => { + check_attributes(); + assignContextMenu(this); + if (!this.isBatchMode()) + this.draw_g.on('click', evnt => this.handleMouseClick(evnt)); + return this; + }); } static async draw(dom, obj) { @@ -170,4 +207,5 @@ class TWebPaintingPainter extends ObjectPainter { } // class TWebPaintingPainter + export { TWebPaintingPainter }; diff --git a/js/modules/gpad/RPadPainter.mjs b/js/modules/gpad/RPadPainter.mjs index 3cf998be987b6..eaae5cf085eab 100644 --- a/js/modules/gpad/RPadPainter.mjs +++ b/js/modules/gpad/RPadPainter.mjs @@ -812,8 +812,11 @@ class RPadPainter extends RObjectPainter { /** @summary Redraw legend object * @desc Used when object attributes are changed to ensure that legend is up to date * @private */ - async redrawLegend() { - } + async redrawLegend() {} + + /** @summary Deliver mouse move or click event to the web canvas + * @private */ + deliverWebCanvasEvent() {} /** @summary Redraw pad means redraw ourself * @return {Promise} when redrawing ready */ diff --git a/js/modules/gpad/TFramePainter.mjs b/js/modules/gpad/TFramePainter.mjs index 83517eac0f2ae..b662dd07edc69 100644 --- a/js/modules/gpad/TFramePainter.mjs +++ b/js/modules/gpad/TFramePainter.mjs @@ -392,11 +392,13 @@ const TooltipHandler = { } } - let nhints = 0, nexact = 0, maxlen = 0, lastcolor1 = 0, usecolor1 = false, textheight = 11; + let nhints = 0, nexact = 0, maxlen = 0, lastcolor1 = 0, usecolor1 = false; const hmargin = 3, wmargin = 3, hstep = 1.2, frame_rect = this.getFrameRect(), pp = this.getPadPainter(), pad_width = pp?.getPadWidth(), + scale = this.getCanvPainter()?.getPadScale() ?? 1, + textheight = (pnt?.touch ? 15 : 11) * scale, font = new FontHandler(160, textheight), disable_tootlips = !this.isTooltipAllowed() || !this.tooltip_enabled; @@ -408,10 +410,8 @@ const TooltipHandler = { // collect tooltips from pad painter - it has list of all drawn objects const hints = pp?.processPadTooltipEvent(pnt) ?? []; - if (pp?._deliver_webcanvas_events && pp?.is_active_pad && pnt && isFunc(pp?.deliverWebCanvasEvent)) - pp.deliverWebCanvasEvent('move', frame_rect.x + pnt.x, frame_rect.y + pnt.y, hints); - - if (pnt?.touch) textheight = 15; + if (pnt && frame_rect) + pp.deliverWebCanvasEvent('move', frame_rect.x + pnt.x, frame_rect.y + pnt.y, hints ? hints[0]?.painter?.snapid : ''); for (let n = 0; n < hints.length; ++n) { const hint = hints[n]; @@ -445,7 +445,8 @@ const TooltipHandler = { hint.height = Math.round(hint.lines.length * textheight * hstep + 2 * hmargin - textheight * (hstep - 1)); if ((hint.color1 !== undefined) && (hint.color1 !== 'none')) { - if ((lastcolor1 !== 0) && (lastcolor1 !== hint.color1)) usecolor1 = true; + if ((lastcolor1 !== 0) && (lastcolor1 !== hint.color1)) + usecolor1 = true; lastcolor1 = hint.color1; } } @@ -476,21 +477,27 @@ const TooltipHandler = { if (!hint) hint = hints[k]; // select exact hint if this is the only one - if (hints[k].exact && (nexact < 2) && (!hint || !hint.exact)) { hint = hints[k]; break; } + if (hints[k].exact && (nexact < 2) && (!hint || !hint.exact)) { + hint = hints[k]; + break; + } - if (!pnt || (hints[k].x === undefined) || (hints[k].y === undefined)) continue; + if (!pnt || (hints[k].x === undefined) || (hints[k].y === undefined)) + continue; const dist2 = (pnt.x - hints[k].x) ** 2 + (pnt.y - hints[k].y) ** 2; if (dist2 < best_dist2) { best_dist2 = dist2; best_hint = hints[k]; } } - if ((!hint || !hint.exact) && (best_dist2 < 400)) hint = best_hint; + if ((!hint || !hint.exact) && (best_dist2 < 400)) + hint = best_hint; if (hint) { name = (hint.lines && hint.lines.length > 1) ? hint.lines[0] : hint.name; title = hint.title || ''; info = hint.line; - if (!info && hint.lines) info = hint.lines.slice(1).join(' '); + if (!info && hint.lines) + info = hint.lines.slice(1).join(' '); } this.showObjectStatus(name, title, info, coordinates); @@ -522,7 +529,7 @@ const TooltipHandler = { .property('hints_pad', this.getPadName()); let viewmode = hintsg.property('viewmode') || '', - actualw = 0, posx = pnt.x + frame_rect.hint_delta_x; + actualw = 0, posx = pnt.x + frame_rect.hint_delta_x; if (show_only_best || (nhints === 1)) { viewmode = 'single'; @@ -592,7 +599,8 @@ const TooltipHandler = { n = -1; } } - if ((gapminx === -1111) && (gapmaxx === -1111)) gapminx = gapmaxx = hint.x; + if ((gapminx === -1111) && (gapmaxx === -1111)) + gapminx = gapmaxx = hint.x; gapminx = Math.min(gapminx, hint.x); gapmaxx = Math.min(gapmaxx, hint.x); } diff --git a/js/modules/gpad/TPadPainter.mjs b/js/modules/gpad/TPadPainter.mjs index 1a64ba4578dba..8c2ec46987715 100644 --- a/js/modules/gpad/TPadPainter.mjs +++ b/js/modules/gpad/TPadPainter.mjs @@ -144,7 +144,7 @@ const PadButtonsHandler = { item.tooltip + (iscan ? '' : (` on pad ${this.this_pad_name}`)) + (item.keyname ? ` (keyshortcut ${item.keyname})` : ''), false); if (group.property('vertical')) - svg.attr('x', y).attr('y', x); + svg.attr('x', y).attr('y', x); else svg.attr('x', x).attr('y', y); @@ -318,6 +318,9 @@ class TPadPainter extends ObjectPainter { /** @summary get pad height */ getPadHeight() { return this._pad_height || 0; } + /** @summary get pad height */ + getPadScale() { return this._pad_scale || 1; } + /** @summary get pad rect */ getPadRect() { return { @@ -713,35 +716,36 @@ class TPadPainter extends ObjectPainter { svg.style('filter', settings.DarkMode || this.pad?.$dark ? 'invert(100%)' : null); - svg.attr('viewBox', `0 0 ${rect.width} ${rect.height}`) - .attr('preserveAspectRatio', 'none') // we do not preserve relative ratio - .property('height_factor', factor) - .property('draw_x', 0) - .property('draw_y', 0) - .property('draw_width', rect.width) - .property('draw_height', rect.height); - + this._pad_scale = settings.CanvasScale || 1; this._pad_x = 0; this._pad_y = 0; - this._pad_width = rect.width; - this._pad_height = rect.height; + this._pad_width = rect.width * this._pad_scale; + this._pad_height = rect.height * this._pad_scale; + + svg.attr('viewBox', `0 0 ${this._pad_width} ${this._pad_height}`) + .attr('preserveAspectRatio', 'none') // we do not preserve relative ratio + .property('height_factor', factor) + .property('draw_x', this._pad_x) + .property('draw_y', this._pad_y) + .property('draw_width', this._pad_width) + .property('draw_height', this._pad_height); this.addPadBorder(svg, frect); - this.setFastDrawing(rect.width * (1 - this.pad.fLeftMargin - this.pad.fRightMargin), rect.height * (1 - this.pad.fBottomMargin - this.pad.fTopMargin)); + this.setFastDrawing(this._pad_width * (1 - this.pad.fLeftMargin - this.pad.fRightMargin), this._pad_height * (1 - this.pad.fBottomMargin - this.pad.fTopMargin)); if (this.alignButtons && btns) - this.alignButtons(btns, rect.width, rect.height); + this.alignButtons(btns, this._pad_width, this._pad_height); let dt = info.selectChild('.canvas_date'); if (!gStyle.fOptDate) dt.remove(); else { if (dt.empty()) - dt = info.append('text').attr('class', 'canvas_date'); - const posy = Math.round(rect.height * (1 - gStyle.fDateY)), + dt = info.append('text').attr('class', 'canvas_date'); + const posy = Math.round(this._pad_height * (1 - gStyle.fDateY)), date = new Date(); - let posx = Math.round(rect.width * gStyle.fDateX); + let posx = Math.round(this._pad_width * gStyle.fDateX); if (!is_batch && (posx < 25)) posx = 25; if (gStyle.fOptDate > 3) @@ -898,6 +902,7 @@ class TPadPainter extends ObjectPainter { .property('draw_width', w) .property('draw_height', h); + this._pad_scale = 1; // subpads always use scale 1 while placed inside canvas viewBox this._pad_x = x; this._pad_y = y; this._pad_width = w; @@ -914,7 +919,7 @@ class TPadPainter extends ObjectPainter { } if (this.alignButtons && btns) - this.alignButtons(btns, w, h); + this.alignButtons(btns, this._pad_width, this._pad_height); return pad_visible; } @@ -1914,7 +1919,7 @@ class TPadPainter extends ObjectPainter { first.fPrimitives = null; // primitives are not interesting, they are disabled in IO // if there are execs in the pad, deliver events to the server - this._deliver_webcanvas_events = first.fExecs?.arr?.length > 0; + this._deliver_move_events = first.fExecs?.arr?.length > 0; if (this.snapid === undefined) { // first time getting snap, create all gui elements first @@ -2051,7 +2056,8 @@ class TPadPainter extends ObjectPainter { fp.cleanFrameDrawings(); fp.redraw(); } - if (isFunc(this.removePadButtons)) this.removePadButtons(); + if (isFunc(this.removePadButtons)) + this.removePadButtons(); this.addPadButtons(true); } @@ -2067,16 +2073,16 @@ class TPadPainter extends ObjectPainter { /** @summary Deliver mouse move or click event to the web canvas * @private */ - deliverWebCanvasEvent(kind, x, y, hints) { - if (!this._deliver_webcanvas_events || !this.is_active_pad || this.doingDraw() || x === undefined || y === undefined) return; + deliverWebCanvasEvent(kind, x, y, snapid) { + if (!this.is_active_pad || this.doingDraw() || x === undefined || y === undefined) + return; + if ((kind === 'move') && !this._deliver_move_events) + return; const cp = this.getCanvPainter(); - if (!cp || !cp._websocket || !cp._websocket.canSend(2) || cp._readonly) return; - - let selobj_snapid = ''; - if (hints && hints[0] && hints[0].painter?.snapid) - selobj_snapid = hints[0].painter.snapid.toString(); + if (!cp || !cp._websocket || !cp._websocket.canSend(2) || cp._readonly) + return; - const msg = JSON.stringify([this.snapid, kind, x.toString(), y.toString(), selobj_snapid]); + const msg = JSON.stringify([this.snapid, kind, x.toString(), y.toString(), snapid ? snapid.toString() : '']); cp.sendWebsocket(`EVENT:${msg}`); } @@ -2393,16 +2399,24 @@ class TPadPainter extends ObjectPainter { .attr('href', dataUrl); }, 'pads'); - let width = elem.property('draw_width'), height = elem.property('draw_height'); + let width = elem.property('draw_width'), + height = elem.property('draw_height'), + viewBox = ''; if (use_frame) { const fp = this.getFramePainter(); width = fp.getFrameWidth(); height = fp.getFrameHeight(); } + const scale = this.getCanvPainter()?.getPadScale() ?? 1; + if (scale !== 1) { + viewBox = `viewBox="0 0 ${width} ${height}"`; + width = Math.round(width / scale); + height = Math.round(height / scale); + } const arg = (file_format === 'pdf') - ? { node: elem.node(), width, height, reset_tranform: use_frame } - : compressSVG(`${elem.node().innerHTML}`); + ? { node: elem.node(), width, height, scale, reset_tranform: use_frame } + : compressSVG(`${elem.node().innerHTML}`); return svgToImage(arg, file_format, args).then(res => { // reactivate border @@ -2472,10 +2486,16 @@ class TPadPainter extends ObjectPainter { const shown = []; this.painters.forEach((pp, indx) => { const obj = pp?.getObject(); - if (!obj || (shown.indexOf(obj) >= 0)) return; - let name = isFunc(pp.getClassName) ? pp.getClassName() : (obj._typename || ''); - if (name) name += '::'; - name += isFunc(pp.getObjectName) ? pp.getObjectName() : (obj.fName || `item${indx}`); + if (!obj || (shown.indexOf(obj) >= 0)) + return; + let name = ''; + if (isFunc(pp.getMenuHeader)) + name = pp.getMenuHeader(); + else { + name = isFunc(pp.getClassName) ? pp.getClassName() : (obj._typename || ''); + if (name) name += '::'; + name += isFunc(pp.getObjectName) ? pp.getObjectName() : (obj.fName || `item${indx}`); + } menu.add(name, indx, this.itemContextMenu); shown.push(obj); }); diff --git a/js/modules/gui.mjs b/js/modules/gui.mjs index 322325381646d..6573f26d0e90d 100644 --- a/js/modules/gui.mjs +++ b/js/modules/gui.mjs @@ -42,6 +42,11 @@ function readStyleFromURL(url) { } } + if (d.has('scale')) { + const s = parseInt(d.get('scale')); + settings.CanvasScale = Number.isInteger(s) ? s : 2; + } + const b = d.get('batch'); if (b !== undefined) { setBatchMode(d !== 'off'); diff --git a/js/modules/hist/TPavePainter.mjs b/js/modules/hist/TPavePainter.mjs index 8ac43341fc6d1..85968ca3d919a 100644 --- a/js/modules/hist/TPavePainter.mjs +++ b/js/modules/hist/TPavePainter.mjs @@ -613,7 +613,7 @@ class TPavePainter extends ObjectPainter { arg.y = texty + 0.05 * stepy; arg.height = 0.9*stepy; // prevent expand of normal title on full width - // if (this.isTitle() && (halign === 2) && (arg.width > 0.1*pad_width) && (arg.width < 0.7*pad_width)) { + // if (this.isTitle() && (halign === 2) && (arg.width > 0.1*pad_width) && (arg.width < 0.7*pad_width)) { // arg.width -= 0.02*pad_width; // arg.x = 0.01*pad_width; // } diff --git a/js/modules/hist/hist3d.mjs b/js/modules/hist/hist3d.mjs index 61dd68a01ab7d..9e6a31a5ff09c 100644 --- a/js/modules/hist/hist3d.mjs +++ b/js/modules/hist/hist3d.mjs @@ -407,6 +407,13 @@ function create3DControl(fp) { const frame_painter = fp, obj_painter = fp.getMainPainter(); + if (fp.access3dKind() === constants.Embed3D.Embed) { + // tooltip scaling only need when GL canvas embed into + const scale = fp.getCanvPainter()?.getPadScale(); + if (scale) + fp.control.tooltip?.setScale(scale); + } + fp.control.processMouseMove = function(intersects) { let tip = null, mesh = null, zoom_mesh = null; const handle_tooltip = frame_painter.isTooltipAllowed(); @@ -521,7 +528,7 @@ function create3DScene(render3d, x3dscale, y3dscale, orthographic) { disposeThreejsObject(this.toplevel); delete this.tooltip_mesh; delete this.toplevel; - if (this.control) this.control.HideTooltip(); + this.control?.hideTooltip(); const newtop = new THREE.Object3D(); this.scene.add(newtop); @@ -568,7 +575,7 @@ function create3DScene(render3d, x3dscale, y3dscale, orthographic) { }).then(r => { this.renderer = r; - this.webgl = (r.jsroot_render3d === constants.Render3D.WebGL); + this.webgl = r.jsroot_render3d === constants.Render3D.WebGL; this.add3dCanvas(sz, r.jsroot_dom, this.webgl); this.first_render_tm = 0; @@ -706,9 +713,11 @@ function resize3D() { this.apply3dSize(sz); - if ((this.scene_width === sz.width) && (this.scene_height === sz.height)) return false; + if ((this.scene_width === sz.width) && (this.scene_height === sz.height)) + return false; - if ((sz.width < 10) || (sz.height < 10)) return false; + if ((sz.width < 10) || (sz.height < 10)) + return false; this.scene_width = sz.width; this.scene_height = sz.height; From 9c139b8ee4036eb4eae614df153c8c06fb7c773a Mon Sep 17 00:00:00 2001 From: Jonas Hahnfeld Date: Wed, 11 Dec 2024 09:06:04 +0100 Subject: [PATCH 11/12] [cling] Prevent local optimizations of definitions In case of weak definitions, JITLink might merge symbols because we currently add all llvm::Module's into a single JITDylib. For merges that late in the pipeline, we also have to prevent optimizations that take advantage of "localness" to avoid out-of-range relocations for example on AArch64. --- .../cling/lib/Interpreter/BackendPasses.cpp | 27 +++++++++++-------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/interpreter/cling/lib/Interpreter/BackendPasses.cpp b/interpreter/cling/lib/Interpreter/BackendPasses.cpp index 0271f526e0243..fac174258b52a 100644 --- a/interpreter/cling/lib/Interpreter/BackendPasses.cpp +++ b/interpreter/cling/lib/Interpreter/BackendPasses.cpp @@ -151,19 +151,24 @@ namespace { namespace { class PreventLocalOptPass : public PassInfoMixin { bool runOnGlobal(GlobalValue& GV) { - if (!GV.isDeclaration()) - return false; // no change. - - // GV is a declaration with no definition. Make sure to prevent any - // optimization that tries to take advantage of the actual definition - // being "local" because we have no influence on the memory layout of - // data sections and how "close" they are to the code. - bool changed = false; + // Prevent any optimization that tries to take advantage of the actual + // definition being "local" because we have no influence on the memory + // layout of sections and how "close" they are. + if (GV.hasLocalLinkage()) { - GV.setLinkage(llvm::GlobalValue::ExternalLinkage); - changed = true; + if (GV.isDeclaration()) { + // For declarations with no definition, we can simply adjust the + // linkage. + GV.setLinkage(llvm::GlobalValue::ExternalLinkage); + changed = true; + } else { + // FIXME: Not clear what would be the right linkage. We also cannot + // continue because "GlobalValue with local linkage [...] must be + // dso_local!" + return false; + } } if (!GV.hasDefaultVisibility()) { @@ -172,7 +177,7 @@ namespace { } // Set DSO locality last because setLinkage() and setVisibility() check - // isImplicitDSOLocal(). + // isImplicitDSOLocal() and then might call setDSOLocal(true). if (GV.isDSOLocal()) { GV.setDSOLocal(false); changed = true; From b0121698f19d02b7a40c1dc23b2dd23b1d510f57 Mon Sep 17 00:00:00 2001 From: Jonas Hahnfeld Date: Tue, 10 Sep 2024 14:45:38 +0200 Subject: [PATCH 12/12] [runtime_cxxmodules] Enable on AArch64 It was disabled in commit a67863d33a ("Disable modules on aarch64 due to ODR violation") in 2019. I cannot reproduce these problems on lxplus-arm, so try to turn it back on. --- cmake/modules/RootBuildOptions.cmake | 6 ------ 1 file changed, 6 deletions(-) diff --git a/cmake/modules/RootBuildOptions.cmake b/cmake/modules/RootBuildOptions.cmake index 16e73d8d098c5..3d5625e81d115 100644 --- a/cmake/modules/RootBuildOptions.cmake +++ b/cmake/modules/RootBuildOptions.cmake @@ -318,12 +318,6 @@ elseif(APPLE) set(x11_defvalue OFF) endif() -# Current limitations for modules: -#---Modules are disabled on aarch64 platform (due ODR violations) -if(CMAKE_SYSTEM_PROCESSOR MATCHES aarch64) - set(runtime_cxxmodules_defvalue OFF) -endif() - # builtin_openssl is only supported on macOS if(builtin_openssl AND NOT APPLE) message(FATAL_ERROR ">>> Option 'builtin_openssl' is only supported on macOS.")