use interval tree for list of spanners
This commit is contained in:
parent
30fc3f20fc
commit
3f63dcd6d4
25 changed files with 528 additions and 79 deletions
|
@ -76,7 +76,7 @@ add_library (
|
|||
cursor.cpp read114.cpp paste.cpp
|
||||
bsymbol.cpp marker.cpp jump.cpp stemslash.cpp ledgerline.cpp
|
||||
synthesizerstate.cpp mcursor.cpp groups.cpp mscoreview.cpp
|
||||
noteline.cpp
|
||||
noteline.cpp spannermap.cpp
|
||||
)
|
||||
if (SCRIPT_INTERFACE)
|
||||
set_target_properties (
|
||||
|
|
|
@ -2168,7 +2168,7 @@ QPointF Chord::layoutArticulation(Articulation* a)
|
|||
// reserve space for slur
|
||||
bool botGap = false;
|
||||
bool topGap = false;
|
||||
#if 0 // TODO-S
|
||||
#if 0 // TODO-S: optimize
|
||||
for (Spanner* sp = spannerFor(); sp; sp = sp->next()) {
|
||||
if (sp->type() != SLUR)
|
||||
continue;
|
||||
|
|
|
@ -554,7 +554,7 @@ void ChordRest::layoutArticulations()
|
|||
bool botGap = false;
|
||||
bool topGap = false;
|
||||
|
||||
#if 0 // TODO-S
|
||||
#if 0 // TODO-S: optimize
|
||||
for (Spanner* sp = _spannerFor; sp; sp = sp->next()) {
|
||||
if (sp->type() != SLUR)
|
||||
continue;
|
||||
|
|
|
@ -13,7 +13,6 @@
|
|||
#include "elementmap.h"
|
||||
#include "tupletmap.h"
|
||||
#include "tiemap.h"
|
||||
#include "spannermap.h"
|
||||
#include "slur.h"
|
||||
#include "chordrest.h"
|
||||
|
||||
|
|
|
@ -32,7 +32,6 @@
|
|||
#include "segment.h"
|
||||
#include "tupletmap.h"
|
||||
#include "tiemap.h"
|
||||
#include "spannermap.h"
|
||||
#include "layoutbreak.h"
|
||||
|
||||
namespace Ms {
|
||||
|
|
|
@ -642,7 +642,7 @@ void Score::doLayout()
|
|||
}
|
||||
}
|
||||
|
||||
for (const std::pair<int,Spanner*>& s : _spanner) {
|
||||
for (const std::pair<int,Spanner*>& s : _spanner.map()) {
|
||||
Spanner* sp = s.second;
|
||||
if (sp->tick() == -1 || sp->tick2() == -1) {
|
||||
printf("bad spanner id %d %s %d - %d\n", sp->id(), sp->name(), sp->tick(), sp->tick2());
|
||||
|
|
|
@ -71,7 +71,6 @@
|
|||
#include "tablature.h"
|
||||
#include "tiemap.h"
|
||||
#include "tupletmap.h"
|
||||
#include "spannermap.h"
|
||||
#include "accidental.h"
|
||||
#include "layout.h"
|
||||
#include "icon.h"
|
||||
|
@ -3408,7 +3407,7 @@ Fraction Measure::stretchedLen(Staff* staff) const
|
|||
// cloneMeasure
|
||||
//---------------------------------------------------------
|
||||
|
||||
Measure* Measure::cloneMeasure(Score* sc, TieMap* tieMap, SpannerMap* /*spannerMap*/)
|
||||
Measure* Measure::cloneMeasure(Score* sc, TieMap* tieMap)
|
||||
{
|
||||
Measure* m = new Measure(sc);
|
||||
m->_timesig = _timesig;
|
||||
|
|
|
@ -37,7 +37,6 @@ class System;
|
|||
class Note;
|
||||
class Spacer;
|
||||
class TieMap;
|
||||
class SpannerMap;
|
||||
class AccidentalState;
|
||||
class Spanner;
|
||||
class Part;
|
||||
|
@ -149,7 +148,7 @@ class Measure : public MeasureBase {
|
|||
virtual Measure* clone() const { return new Measure(*this); }
|
||||
virtual ElementType type() const { return MEASURE; }
|
||||
virtual void setScore(Score* s);
|
||||
Measure* cloneMeasure(Score*, TieMap*, SpannerMap*);
|
||||
Measure* cloneMeasure(Score*, TieMap*);
|
||||
|
||||
void read(XmlReader&, int idx);
|
||||
void read(XmlReader& d) { read(d, 0); }
|
||||
|
|
|
@ -161,22 +161,16 @@ LineSegment* Ottava::createLineSegment()
|
|||
|
||||
void Ottava::endEdit()
|
||||
{
|
||||
#if 0 // TODO-S
|
||||
if (oStartElement != startElement() || oEndElement != endElement()) {
|
||||
if (editTick != tick() || editTick2 != tick2()) {
|
||||
Staff* s = staff();
|
||||
int tick1 = static_cast<Segment*>(oStartElement)->tick();
|
||||
int tick2 = static_cast<Segment*>(oEndElement)->tick();
|
||||
s->pitchOffsets().remove(tick1);
|
||||
s->pitchOffsets().remove(tick2);
|
||||
s->pitchOffsets().remove(editTick);
|
||||
s->pitchOffsets().remove(editTick2);
|
||||
|
||||
tick1 = static_cast<Segment*>(startElement())->tick();
|
||||
tick2 = static_cast<Segment*>(endElement())->tick();
|
||||
s->pitchOffsets().setPitchOffset(tick1, _pitchShift);
|
||||
s->pitchOffsets().setPitchOffset(tick2, 0);
|
||||
s->pitchOffsets().setPitchOffset(tick(), _pitchShift);
|
||||
s->pitchOffsets().setPitchOffset(tick2(), 0);
|
||||
|
||||
score()->addLayoutFlags(LAYOUT_FIX_PITCH_VELO);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
|
|
|
@ -143,7 +143,7 @@ void Score::cmdPaste(MuseScoreView* view)
|
|||
|
||||
void Score::pasteStaff(XmlReader& e, ChordRest* dst)
|
||||
{
|
||||
for (auto i :_spanner)
|
||||
for (auto i :_spanner.map())
|
||||
i.second->setId(-1);
|
||||
|
||||
QList<Chord*> graceNotes;
|
||||
|
|
|
@ -676,7 +676,7 @@ void Score::createPlayEvents(Chord* chord)
|
|||
|
||||
int tick = chord->tick();
|
||||
Slur* slur = 0;
|
||||
for (auto sp : _spanner) {
|
||||
for (auto sp : _spanner.map()) {
|
||||
if (sp.second->type() != Element::SLUR || sp.second->track() != chord->track())
|
||||
continue;
|
||||
Slur* s = static_cast<Slur*>(sp.second);
|
||||
|
@ -804,7 +804,7 @@ void Score::renderMidi(EventMap* events)
|
|||
int utick1 = rs->utick;
|
||||
int utick2 = utick1 + rs->len;
|
||||
|
||||
for (std::pair<int,Spanner*> sp : _spanner) {
|
||||
for (std::pair<int,Spanner*> sp : _spanner.map()) {
|
||||
Spanner* s = sp.second;
|
||||
if (s->type() != Element::PEDAL)
|
||||
continue;
|
||||
|
|
|
@ -28,7 +28,7 @@ namespace Ms {
|
|||
|
||||
Volta* Score::searchVolta(int tick) const
|
||||
{
|
||||
for (const std::pair<int,Spanner*>& p : _spanner) {
|
||||
for (const std::pair<int,Spanner*>& p : _spanner.map()) {
|
||||
Spanner* s = p.second;
|
||||
if (s->type() != Element::VOLTA)
|
||||
continue;
|
||||
|
|
|
@ -56,7 +56,6 @@
|
|||
#include "articulation.h"
|
||||
#include "revisions.h"
|
||||
#include "tiemap.h"
|
||||
#include "spannermap.h"
|
||||
#include "layoutbreak.h"
|
||||
#include "harmony.h"
|
||||
#include "mscore.h"
|
||||
|
@ -1371,7 +1370,6 @@ void Score::addElement(Element* element)
|
|||
}
|
||||
break;
|
||||
|
||||
#if 0 // TODO-S
|
||||
case Element::OTTAVA:
|
||||
{
|
||||
Ottava* o = static_cast<Ottava*>(element);
|
||||
|
@ -1380,23 +1378,18 @@ void Score::addElement(Element* element)
|
|||
ss->system()->add(ss);
|
||||
}
|
||||
Staff* s = o->staff();
|
||||
if (o->startElement()) {
|
||||
int tick = static_cast<Segment*>(o->startElement())->tick();
|
||||
s->pitchOffsets().setPitchOffset(tick, o->pitchShift());
|
||||
}
|
||||
if (o->endElement()) {
|
||||
int tick = static_cast<Segment*>(o->endElement())->tick();
|
||||
s->pitchOffsets().setPitchOffset(tick, 0);
|
||||
}
|
||||
s->pitchOffsets().setPitchOffset(o->tick(), o->pitchShift());
|
||||
s->pitchOffsets().setPitchOffset(o->tick2(), 0);
|
||||
layoutFlags |= LAYOUT_FIX_PITCH_VELO;
|
||||
_playlistDirty = true;
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
|
||||
case Element::DYNAMIC:
|
||||
layoutFlags |= LAYOUT_FIX_PITCH_VELO;
|
||||
_playlistDirty = true;
|
||||
break;
|
||||
|
||||
case Element::CLEF:
|
||||
{
|
||||
Clef* clef = static_cast<Clef*>(element);
|
||||
|
@ -1404,6 +1397,7 @@ void Score::addElement(Element* element)
|
|||
updateNoteLines(clef->segment(), clef->track());
|
||||
}
|
||||
break;
|
||||
|
||||
case Element::KEYSIG:
|
||||
{
|
||||
KeySig* ks = static_cast<KeySig*>(element);
|
||||
|
@ -1415,12 +1409,14 @@ void Score::addElement(Element* element)
|
|||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case Element::TEMPO_TEXT:
|
||||
{
|
||||
TempoText* tt = static_cast<TempoText*>(element);
|
||||
setTempo(tt->segment(), tt->tempo());
|
||||
}
|
||||
break;
|
||||
|
||||
case Element::INSTRUMENT_CHANGE:
|
||||
rebuildMidiMapping();
|
||||
_instrumentsChanged = true;
|
||||
|
@ -1515,7 +1511,6 @@ void Score::removeElement(Element* element)
|
|||
}
|
||||
break;
|
||||
|
||||
#if 0 // TODO-S
|
||||
case Element::OTTAVA:
|
||||
{
|
||||
Ottava* o = static_cast<Ottava*>(element);
|
||||
|
@ -1524,15 +1519,13 @@ void Score::removeElement(Element* element)
|
|||
ss->system()->remove(ss);
|
||||
}
|
||||
Staff* s = o->staff();
|
||||
int tick1 = static_cast<Segment*>(o->startElement())->tick();
|
||||
int tick2 = static_cast<Segment*>(o->endElement())->tick();
|
||||
s->pitchOffsets().remove(tick1);
|
||||
s->pitchOffsets().remove(tick2);
|
||||
s->pitchOffsets().remove(o->tick());
|
||||
s->pitchOffsets().remove(o->tick2());
|
||||
layoutFlags |= LAYOUT_FIX_PITCH_VELO;
|
||||
_playlistDirty = true;
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
|
||||
case Element::DYNAMIC:
|
||||
layoutFlags |= LAYOUT_FIX_PITCH_VELO;
|
||||
_playlistDirty = true;
|
||||
|
@ -2170,13 +2163,12 @@ void Score::removeAudio()
|
|||
bool Score::appendScore(Score* score)
|
||||
{
|
||||
TieMap tieMap;
|
||||
SpannerMap spannerMap;
|
||||
|
||||
MeasureBaseList* ml = &score->_measures;
|
||||
for (MeasureBase* mb = ml->first(); mb; mb = mb->next()) {
|
||||
MeasureBase* nmb;
|
||||
if (mb->type() == Element::MEASURE)
|
||||
nmb = static_cast<Measure*>(mb)->cloneMeasure(this, &tieMap, &spannerMap);
|
||||
nmb = static_cast<Measure*>(mb)->cloneMeasure(this, &tieMap);
|
||||
else
|
||||
nmb = mb->clone();
|
||||
nmb->setNext(0);
|
||||
|
@ -3341,7 +3333,7 @@ Cursor* Score::newCursor()
|
|||
|
||||
void Score::addSpanner(Spanner* s)
|
||||
{
|
||||
_spanner.insert(std::pair<int,Spanner*>(s->tick(), s));
|
||||
_spanner.addSpanner(s);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
|
@ -3350,14 +3342,7 @@ void Score::addSpanner(Spanner* s)
|
|||
|
||||
void Score::removeSpanner(Spanner* s)
|
||||
{
|
||||
// for (auto i = _spanner.lower_bound(s->tick()); i != _spanner.upper_bound(s->tick()); ++i) {
|
||||
for (auto i = _spanner.begin(); i != _spanner.end(); ++i) {
|
||||
if (i->second == s) {
|
||||
_spanner.erase(i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
qDebug("Score::removeSpanner: %s not found", s->name());
|
||||
_spanner.removeSpanner(s);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
|
@ -3366,7 +3351,7 @@ void Score::removeSpanner(Spanner* s)
|
|||
|
||||
Spanner* Score::findSpanner(int id) const
|
||||
{
|
||||
for (auto i = _spanner.rbegin(); i != _spanner.rend(); ++i) {
|
||||
for (auto i = _spanner.crbegin(); i != _spanner.crend(); ++i) {
|
||||
if (i->second->id() == id)
|
||||
return i->second;
|
||||
}
|
||||
|
@ -3381,7 +3366,7 @@ Spanner* Score::findSpanner(int id) const
|
|||
|
||||
bool Score::isSpannerStartEnd(int tick, int track) const
|
||||
{
|
||||
for (auto i : _spanner) {
|
||||
for (auto i : _spanner.map()) {
|
||||
if (i.second->track() != track)
|
||||
continue;
|
||||
if (i.second->tick() == tick || i.second->tick2() == tick)
|
||||
|
@ -3399,12 +3384,10 @@ void Score::insertTime(int tick, int len)
|
|||
if (len == 0)
|
||||
return;
|
||||
|
||||
printf("insertTime score %p at %d len %d\n", this, tick, len);
|
||||
for (auto i : _spanner) {
|
||||
for (auto i : _spanner.map()) {
|
||||
Spanner* s = i.second;
|
||||
if (s->tick2() < tick)
|
||||
continue;
|
||||
printf(" %p score %p change spanner %d+%d\n", s, s->score(), s->tick(), s->tickLen());
|
||||
if (len > 0) {
|
||||
if (tick > s->tick() && tick < s->tick2()) {
|
||||
//
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
#include "segment.h"
|
||||
#include "accidental.h"
|
||||
#include "note.h"
|
||||
#include "spannermap.h"
|
||||
|
||||
class QPainter;
|
||||
|
||||
|
@ -262,7 +263,7 @@ class Score : public QObject {
|
|||
int _pageNumberOffset; ///< Offset for page numbers.
|
||||
|
||||
MeasureBaseList _measures; // here are the notes
|
||||
std::multimap<int, Spanner*> _spanner; // spanner map, key is the start tick of the spanner
|
||||
SpannerMap _spanner;
|
||||
//
|
||||
// generated objects during layout:
|
||||
//
|
||||
|
@ -929,8 +930,8 @@ class Score : public QObject {
|
|||
|
||||
qreal noteHeadWidth() const { return _noteHeadWidth; }
|
||||
|
||||
std::multimap<int, Spanner*>& spanner() { return _spanner; }
|
||||
const std::multimap<int, Spanner*>& spanner() const { return _spanner; }
|
||||
std::multimap<int, Spanner*>& spanner() { return _spanner.map(); }
|
||||
const std::multimap<int, Spanner*>& spanner() const { return _spanner.map(); }
|
||||
Spanner* findSpanner(int id) const;
|
||||
bool isSpannerStartEnd(int tick, int track) const;
|
||||
void removeSpanner(Spanner*);
|
||||
|
|
|
@ -1184,7 +1184,7 @@ void Score::writeSegments(Xml& xml, const Measure* m, int strack, int etrack,
|
|||
e->write(xml);
|
||||
}
|
||||
if (segment->segmentType() & (Segment::SegChordRest)) {
|
||||
for (auto i : _spanner) { // TODO: dont search whole list
|
||||
for (auto i : _spanner.map()) { // TODO: dont search whole list
|
||||
Spanner* s = i.second;
|
||||
if (s->track() != track || s->generated())
|
||||
continue;
|
||||
|
|
|
@ -106,10 +106,12 @@ class Spanner : public Element {
|
|||
int _tick, _tick2;
|
||||
int _id; // used for xml serialization
|
||||
|
||||
static int editTick, editTick2;
|
||||
static QList<QPointF> userOffsets;
|
||||
static QList<QPointF> userOffsets2;
|
||||
|
||||
protected:
|
||||
static int editTick, editTick2;
|
||||
|
||||
public:
|
||||
Spanner(Score* = 0);
|
||||
Spanner(const Spanner&);
|
||||
|
|
93
libmscore/spannermap.cpp
Normal file
93
libmscore/spannermap.cpp
Normal file
|
@ -0,0 +1,93 @@
|
|||
//=============================================================================
|
||||
// MuseScore
|
||||
// Music Composition & Notation
|
||||
//
|
||||
// Copyright (C) 2013 Werner Schweer
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License version 2
|
||||
// as published by the Free Software Foundation and appearing in
|
||||
// the file LICENCE.GPL
|
||||
//=============================================================================
|
||||
|
||||
#include "spannermap.h"
|
||||
#include "spanner.h"
|
||||
|
||||
namespace Ms {
|
||||
|
||||
//---------------------------------------------------------
|
||||
// SpannerMap
|
||||
//---------------------------------------------------------
|
||||
|
||||
SpannerMap::SpannerMap()
|
||||
: std::multimap<int, Spanner*>()
|
||||
{
|
||||
dirty = true;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// update
|
||||
//---------------------------------------------------------
|
||||
|
||||
void SpannerMap::update() const
|
||||
{
|
||||
std::vector< ::Interval<Spanner*> > intervals;
|
||||
for (auto i : *this)
|
||||
intervals.push_back(Interval<Spanner*>(i.second->tick(), i.second->tick2(), i.second));
|
||||
tree = IntervalTree<Spanner*>(intervals);
|
||||
dirty = false;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// findContained
|
||||
//---------------------------------------------------------
|
||||
|
||||
const std::vector<Interval<Spanner*>>& SpannerMap::findContained(int start, int stop)
|
||||
{
|
||||
if (dirty)
|
||||
update();
|
||||
tree.findContained(start, stop, results);
|
||||
return results;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// findOverlapping
|
||||
//---------------------------------------------------------
|
||||
|
||||
const std::vector<Interval<Spanner*>>& SpannerMap::findOverlapping(int start, int stop)
|
||||
{
|
||||
if (dirty)
|
||||
update();
|
||||
tree.findOverlapping(start, stop, results);
|
||||
return results;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// addSpanner
|
||||
//---------------------------------------------------------
|
||||
|
||||
void SpannerMap::addSpanner(Spanner* s)
|
||||
{
|
||||
insert(std::pair<int,Spanner*>(s->tick(), s));
|
||||
dirty = true;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// removeSpanner
|
||||
//---------------------------------------------------------
|
||||
|
||||
bool SpannerMap::removeSpanner(Spanner* s)
|
||||
{
|
||||
for (auto i = begin(); i != end(); ++i) {
|
||||
if (i->second == s) {
|
||||
erase(i);
|
||||
dirty = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
qDebug("Score::removeSpanner: %s not found", s->name());
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace Ms
|
||||
|
|
@ -13,25 +13,39 @@
|
|||
#ifndef __SPANNERMAP_H__
|
||||
#define __SPANNERMAP_H__
|
||||
|
||||
#include "elementmap.h"
|
||||
|
||||
#include "thirdparty/intervaltree/IntervalTree.h"
|
||||
|
||||
namespace Ms {
|
||||
|
||||
class Spanner;
|
||||
class Element;
|
||||
|
||||
//---------------------------------------------------------
|
||||
// SpannerMap
|
||||
//---------------------------------------------------------
|
||||
|
||||
class SpannerMap : public ElementMap {
|
||||
class SpannerMap : std::multimap<int, Spanner*> {
|
||||
mutable bool dirty;
|
||||
mutable IntervalTree<Spanner*> tree;
|
||||
std::vector< ::Interval<Spanner*> > results;
|
||||
|
||||
void update() const;
|
||||
|
||||
public:
|
||||
SpannerMap() {}
|
||||
Spanner* findNew(Spanner* o) const { return (Spanner*)(ElementMap::findNew((Element*)o)); }
|
||||
void add(Spanner* _o, Spanner* _n) { ElementMap::add((Element*)_o, (Element*)_n); }
|
||||
SpannerMap();
|
||||
const std::vector< ::Interval<Spanner*> >& findContained(int start, int stop);
|
||||
const std::vector< ::Interval<Spanner*> >& findOverlapping(int start, int stop);
|
||||
std::multimap<int, Spanner*>& map() { return *this; }
|
||||
const std::multimap<int, Spanner*>& map() const { return *this; }
|
||||
std::multimap<int,Spanner*>::const_reverse_iterator crbegin() const { return std::multimap<int, Spanner*>::crbegin(); }
|
||||
std::multimap<int,Spanner*>::const_reverse_iterator crend() const { return std::multimap<int, Spanner*>::crend(); }
|
||||
std::multimap<int,Spanner*>::const_iterator cbegin() const { return std::multimap<int, Spanner*>::cbegin(); }
|
||||
std::multimap<int,Spanner*>::const_iterator cend() const { return std::multimap<int, Spanner*>::cend(); }
|
||||
void addSpanner(Spanner* s);
|
||||
bool removeSpanner(Spanner* s);
|
||||
};
|
||||
|
||||
|
||||
} // namespace Ms
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
@ -2636,14 +2636,9 @@ void Score::undoRemoveMeasures(Measure* m1, Measure* m2)
|
|||
int tick1 = m1->tick();
|
||||
int tick2 = m2->endTick();
|
||||
|
||||
auto i = _spanner.lower_bound(tick1);
|
||||
while(i != _spanner.upper_bound(tick2)) {
|
||||
Spanner* s = i->second;
|
||||
++i; //iterate before potentially deleting it
|
||||
if (s->tick() >= tick1 && s->tick() < tick2) {
|
||||
undoRemoveElement(s);
|
||||
}
|
||||
}
|
||||
for (auto i : _spanner.findContained(tick1, tick2))
|
||||
undoRemoveElement(i.value);
|
||||
|
||||
//
|
||||
// handle ties which start before m1 and end in (m1-m2)
|
||||
//
|
||||
|
|
|
@ -577,7 +577,7 @@ void OveToMScore::convertTrackHeader(OVE::Track* track, Part* part){
|
|||
part->setMidiProgram(track->getPatch());
|
||||
|
||||
if (ove_->getShowTransposeTrack() && track->getTranspose() != 0 ) {
|
||||
Interval interval = part->instr()->transpose();
|
||||
Ms::Interval interval = part->instr()->transpose();
|
||||
interval.diatonic = -track->getTranspose();
|
||||
part->instr()->setTranspose(interval);
|
||||
}
|
||||
|
|
219
thirdparty/intervaltree/IntervalTree.h
vendored
Normal file
219
thirdparty/intervaltree/IntervalTree.h
vendored
Normal file
|
@ -0,0 +1,219 @@
|
|||
#ifndef __INTERVAL_TREE_H
|
||||
#define __INTERVAL_TREE_H
|
||||
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
|
||||
|
||||
template <class T, typename K = int>
|
||||
class Interval {
|
||||
public:
|
||||
K start;
|
||||
K stop;
|
||||
T value;
|
||||
Interval(K s, K e, const T& v)
|
||||
: start(s)
|
||||
, stop(e)
|
||||
, value(v)
|
||||
{ }
|
||||
};
|
||||
|
||||
template <class T, typename K = int>
|
||||
int intervalStart(const Interval<T,K>& i) {
|
||||
return i.start;
|
||||
}
|
||||
|
||||
template <class T, typename K = int>
|
||||
int intervalStop(const Interval<T,K>& i) {
|
||||
return i.stop;
|
||||
}
|
||||
|
||||
template <class T, typename K = int>
|
||||
ostream& operator<<(ostream& out, Interval<T,K>& i) {
|
||||
out << "Interval(" << i.start << ", " << i.stop << "): " << i.value;
|
||||
return out;
|
||||
}
|
||||
|
||||
template <class T, typename K = int>
|
||||
class IntervalStartSorter {
|
||||
public:
|
||||
bool operator() (const Interval<T,K>& a, const Interval<T,K>& b) {
|
||||
return a.start < b.start;
|
||||
}
|
||||
};
|
||||
|
||||
template <class T, typename K = int>
|
||||
class IntervalTree {
|
||||
|
||||
public:
|
||||
typedef Interval<T,K> interval;
|
||||
typedef vector<interval> intervalVector;
|
||||
typedef IntervalTree<T,K> intervalTree;
|
||||
|
||||
intervalVector intervals;
|
||||
intervalTree* left;
|
||||
intervalTree* right;
|
||||
int center;
|
||||
|
||||
IntervalTree<T,K>(void)
|
||||
: left(NULL)
|
||||
, right(NULL)
|
||||
, center(0)
|
||||
{ }
|
||||
|
||||
IntervalTree<T,K>(const intervalTree& other) {
|
||||
center = other.center;
|
||||
intervals = other.intervals;
|
||||
if (other.left) {
|
||||
left = (intervalTree*) malloc(sizeof(intervalTree));
|
||||
*left = *other.left;
|
||||
} else {
|
||||
left = NULL;
|
||||
}
|
||||
if (other.right) {
|
||||
right = new intervalTree();
|
||||
*right = *other.right;
|
||||
} else {
|
||||
right = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
IntervalTree<T,K>& operator=(const intervalTree& other) {
|
||||
center = other.center;
|
||||
intervals = other.intervals;
|
||||
if (other.left) {
|
||||
left = new intervalTree();
|
||||
*left = *other.left;
|
||||
} else {
|
||||
left = NULL;
|
||||
}
|
||||
if (other.right) {
|
||||
right = new intervalTree();
|
||||
*right = *other.right;
|
||||
} else {
|
||||
right = NULL;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
IntervalTree<T,K>(
|
||||
intervalVector& ivals,
|
||||
unsigned int depth = 16,
|
||||
unsigned int minbucket = 64,
|
||||
int leftextent = 0,
|
||||
int rightextent = 0,
|
||||
unsigned int maxbucket = 512
|
||||
)
|
||||
: left(NULL)
|
||||
, right(NULL)
|
||||
{
|
||||
|
||||
--depth;
|
||||
if (depth == 0 || (ivals.size() < minbucket && ivals.size() < maxbucket)) {
|
||||
intervals = ivals;
|
||||
} else {
|
||||
if (leftextent == 0 && rightextent == 0) {
|
||||
// sort intervals by start
|
||||
IntervalStartSorter<T,K> intervalStartSorter;
|
||||
sort(ivals.begin(), ivals.end(), intervalStartSorter);
|
||||
}
|
||||
|
||||
int leftp = 0;
|
||||
int rightp = 0;
|
||||
int centerp = 0;
|
||||
|
||||
if (leftextent || rightextent) {
|
||||
leftp = leftextent;
|
||||
rightp = rightextent;
|
||||
} else {
|
||||
leftp = ivals.front().start;
|
||||
vector<K> stops;
|
||||
stops.resize(ivals.size());
|
||||
transform(ivals.begin(), ivals.end(), stops.begin(), intervalStop<T,K>);
|
||||
rightp = *max_element(stops.begin(), stops.end());
|
||||
}
|
||||
|
||||
//centerp = ( leftp + rightp ) / 2;
|
||||
centerp = ivals.at(ivals.size() / 2).start;
|
||||
center = centerp;
|
||||
|
||||
intervalVector lefts;
|
||||
intervalVector rights;
|
||||
|
||||
for (typename intervalVector::iterator i = ivals.begin(); i != ivals.end(); ++i) {
|
||||
interval& interval = *i;
|
||||
if (interval.stop < center) {
|
||||
lefts.push_back(interval);
|
||||
} else if (interval.start > center) {
|
||||
rights.push_back(interval);
|
||||
} else {
|
||||
intervals.push_back(interval);
|
||||
}
|
||||
}
|
||||
|
||||
if (!lefts.empty()) {
|
||||
left = new intervalTree(lefts, depth, minbucket, leftp, centerp);
|
||||
}
|
||||
if (!rights.empty()) {
|
||||
right = new intervalTree(rights, depth, minbucket, centerp, rightp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void findOverlapping(K start, K stop, intervalVector& overlapping) {
|
||||
if (!intervals.empty() && ! (stop < intervals.front().start)) {
|
||||
for (typename intervalVector::iterator i = intervals.begin(); i != intervals.end(); ++i) {
|
||||
interval& interval = *i;
|
||||
if (interval.stop >= start && interval.start <= stop) {
|
||||
overlapping.push_back(interval);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (left && start <= center) {
|
||||
left->findOverlapping(start, stop, overlapping);
|
||||
}
|
||||
|
||||
if (right && stop >= center) {
|
||||
right->findOverlapping(start, stop, overlapping);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void findContained(K start, K stop, intervalVector& contained) {
|
||||
if (!intervals.empty() && ! (stop < intervals.front().start)) {
|
||||
for (typename intervalVector::iterator i = intervals.begin(); i != intervals.end(); ++i) {
|
||||
interval& interval = *i;
|
||||
if (interval.start >= start && interval.stop <= stop) {
|
||||
contained.push_back(interval);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (left && start <= center) {
|
||||
left->findContained(start, stop, contained);
|
||||
}
|
||||
|
||||
if (right && stop >= center) {
|
||||
right->findContained(start, stop, contained);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
~IntervalTree(void) {
|
||||
// traverse the left and right
|
||||
// delete them all the way down
|
||||
if (left) {
|
||||
delete left;
|
||||
}
|
||||
if (right) {
|
||||
delete right;
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#endif
|
19
thirdparty/intervaltree/LICENSE
vendored
Normal file
19
thirdparty/intervaltree/LICENSE
vendored
Normal file
|
@ -0,0 +1,19 @@
|
|||
Copyright (c) 2011 Erik Garrison
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
7
thirdparty/intervaltree/Makefile
vendored
Normal file
7
thirdparty/intervaltree/Makefile
vendored
Normal file
|
@ -0,0 +1,7 @@
|
|||
interval_tree_test: interval_tree_test.cpp IntervalTree.h
|
||||
g++ -Wall interval_tree_test.cpp -o interval_tree_test -std=c++0x
|
||||
|
||||
.PHONY: clean
|
||||
|
||||
clean:
|
||||
rm interval_tree_test
|
41
thirdparty/intervaltree/README
vendored
Normal file
41
thirdparty/intervaltree/README
vendored
Normal file
|
@ -0,0 +1,41 @@
|
|||
Overview:
|
||||
|
||||
An interval tree can be used to efficiently find a set of numeric
|
||||
intervals overlapping or containing another interval.
|
||||
|
||||
This library provides a basic implementation of an interval tree using
|
||||
C++ templates, allowing the insertion of arbitrary types into the
|
||||
tree.
|
||||
|
||||
|
||||
Usage:
|
||||
|
||||
Add #include "IntervalTree.h" to the source files in which you will
|
||||
use the interval tree.
|
||||
|
||||
To make an IntervalTree to contain objects of class T, use:
|
||||
|
||||
vector<Interval<T> > intervals;
|
||||
T a, b, c;
|
||||
intervals.push_back(Interval<T>(2, 10, a));
|
||||
intervals.push_back(Interval<T>(3, 4, b));
|
||||
intervals.push_back(Interval<T>(20, 100, c));
|
||||
IntervalTree<T> tree;
|
||||
tree = IntervalTree<T>(intervals);
|
||||
|
||||
Now, it's possible to query the tree and obtain a set of intervals
|
||||
which are contained within the start and stop coordinates.
|
||||
|
||||
vector<Interval<T> > results;
|
||||
tree.findContained(start, stop, results);
|
||||
cout << "found " << results.size()
|
||||
<< " overlapping intervals" << endl;
|
||||
|
||||
The function IntervalTree::findOverlapping provides a method to find
|
||||
all those intervals which are contained or partially overlap the
|
||||
interval (start, stop).
|
||||
|
||||
|
||||
Author: Erik Garrison <erik.garrison@gmail.com>
|
||||
|
||||
License: MIT
|
85
thirdparty/intervaltree/interval_tree_test.cpp
vendored
Normal file
85
thirdparty/intervaltree/interval_tree_test.cpp
vendored
Normal file
|
@ -0,0 +1,85 @@
|
|||
#include <iostream>
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
#include <random>
|
||||
#include <time.h>
|
||||
#include <assert.h>
|
||||
#include "IntervalTree.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
typedef Interval<bool> interval;
|
||||
typedef vector<interval> intervalVector;
|
||||
typedef IntervalTree<bool> intervalTree;
|
||||
|
||||
template<typename K>
|
||||
K randKey(K floor, K ceiling) {
|
||||
K range = ceiling - floor;
|
||||
return floor + range * ((double) rand() / (double) (RAND_MAX + 1.0));
|
||||
}
|
||||
|
||||
template<class T, typename K>
|
||||
Interval<T,K> randomInterval(K maxStart, K maxLength, K maxStop, const T& value) {
|
||||
K start = randKey<K>(0, maxStart);
|
||||
K stop = min<K>(randKey<K>(start, start + maxLength), maxStop);
|
||||
return Interval<T,K>(start, stop, value);
|
||||
}
|
||||
|
||||
int main() {
|
||||
typedef vector<std::size_t> countsVector;
|
||||
|
||||
srand((unsigned)time(NULL));
|
||||
|
||||
intervalVector intervals;
|
||||
intervalVector queries;
|
||||
|
||||
// generate a test set of target intervals
|
||||
for (int i = 0; i < 10000; ++i) {
|
||||
intervals.push_back(randomInterval<bool>(100000, 1000, 100000 + 1, true));
|
||||
}
|
||||
// and queries
|
||||
for (int i = 0; i < 5000; ++i) {
|
||||
queries.push_back(randomInterval<bool>(100000, 1000, 100000 + 1, true));
|
||||
}
|
||||
|
||||
typedef chrono::high_resolution_clock Clock;
|
||||
typedef chrono::milliseconds milliseconds;
|
||||
|
||||
// using brute-force search
|
||||
countsVector bruteforcecounts;
|
||||
Clock::time_point t0 = Clock::now();
|
||||
for (intervalVector::iterator q = queries.begin(); q != queries.end(); ++q) {
|
||||
intervalVector results;
|
||||
for (intervalVector::iterator i = intervals.begin(); i != intervals.end(); ++i) {
|
||||
if (i->start >= q->start && i->stop <= q->stop) {
|
||||
results.push_back(*i);
|
||||
}
|
||||
}
|
||||
bruteforcecounts.push_back(results.size());
|
||||
}
|
||||
Clock::time_point t1 = Clock::now();
|
||||
milliseconds ms = chrono::duration_cast<milliseconds>(t1 - t0);
|
||||
cout << "brute force:\t" << ms.count() << "ms" << endl;
|
||||
|
||||
// using the interval tree
|
||||
intervalTree tree = intervalTree(intervals);
|
||||
countsVector treecounts;
|
||||
t0 = Clock::now();
|
||||
for (intervalVector::iterator q = queries.begin(); q != queries.end(); ++q) {
|
||||
intervalVector results;
|
||||
tree.findContained(q->start, q->stop, results);
|
||||
treecounts.push_back(results.size());
|
||||
}
|
||||
t1 = Clock::now();
|
||||
ms = std::chrono::duration_cast<milliseconds>(t1 - t0);
|
||||
cout << "interval tree:\t" << ms.count() << "ms" << endl;
|
||||
|
||||
// check that the same number of results are returned
|
||||
countsVector::iterator b = bruteforcecounts.begin();
|
||||
for (countsVector::iterator t = treecounts.begin(); t != treecounts.end(); ++t, ++b) {
|
||||
assert(*b == *t);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
Loading…
Reference in a new issue