/*
* SmpMap.java
* (FScape)
*
* Copyright (c) 2001-2016 Hanns Holger Rutz. All rights reserved.
*
* This software is published under the GNU General Public License v3+
*
*
* For further information, please contact Hanns Holger Rutz at
* contact@sciss.de
*/
package de.sciss.fscape.util;
import java.util.*;
/**
* @version 0.71, 14-Nov-07
*/
public class SmpMap
implements Cloneable
{
// -------- public variables --------
/**
* READ-ONLY!
*/
public ParamSpace hSpace;
public ParamSpace vSpace;
public int type;
// -------- private variables --------
protected Vector smps; // Element = SmpZones
// -------- public methods --------
public SmpMap( ParamSpace hSpace, ParamSpace vSpace, int type )
{
this.hSpace = hSpace;
this.vSpace = vSpace;
this.type = type;
smps = new Vector();
}
public SmpMap( ParamSpace hSpace, ParamSpace vSpace )
{
this( hSpace, vSpace, 0 );
}
/**
* Clont vorgegebene SmpMap
*/
public SmpMap( SmpMap src )
{
this.hSpace = src.hSpace;
this.vSpace = src.vSpace;
this.type = src.type;
smps = (Vector) src.smps.clone();
}
public Object clone()
{
return new SmpMap( this );
}
/**
* Fuegt eine neue Sample-Zone in der Karte ein
*
* @return Index in der Sample-Kette; -1 wenn sich die Zone
* mit bereits vorhandenen ueberschneidet oder die
* Zone ausserhalb der Karte liegt
*/
public int addSample( SmpZone smp )
{
Param freqHi, freqLo, velHi, velLo;
double nFreqHi, nFreqLo, nVelHi, nVelLo; // neighbour values
double dist, nDist; // distance from topleft marks Vector-index; don't need to take sqrt()
double d;
int index = -1;
SmpZone neighbour;
freqHi = Param.transform( smp.freqHi, vSpace.unit, null, null );
freqLo = Param.transform( smp.freqLo, vSpace.unit, null, null );
velHi = Param.transform( smp.velHi, hSpace.unit, null, null );
velLo = Param.transform( smp.velLo, hSpace.unit, null, null );
if( (freqHi != null) && vSpace.contains( freqHi.value) &&
(freqLo != null) && vSpace.contains( freqLo.value) &&
(velHi != null) && hSpace.contains( velHi.value) &&
(velLo != null) && hSpace.contains( velLo.value)) {
d = (freqHi.value - vSpace.min) / (vSpace.max - vSpace.min);
dist = d*d;
d = (velHi.value - hSpace.min) / (hSpace.max - hSpace.min);
dist += d*d;
index = this.smps.size();
for( int i = 0; i < this.smps.size(); i++ ) {
neighbour = (SmpZone) this.smps.elementAt( i );
nFreqHi = Param.transform( neighbour.freqHi, vSpace.unit, null, null ).value;
nFreqLo = Param.transform( neighbour.freqLo, vSpace.unit, null, null ).value;
nVelHi = Param.transform( neighbour.velHi, hSpace.unit, null, null ).value;
nVelLo = Param.transform( neighbour.velLo, hSpace.unit, null, null ).value;
if( index > i ) {
d = (nFreqHi - vSpace.min) / (vSpace.max - vSpace.min);
nDist = d*d;
d = (nVelHi - hSpace.min) / (hSpace.max - hSpace.min);
nDist += d*d;
if( dist <= nDist ) {
index = i; // all following nDists are surely greater
}
}
if( (nVelLo < (velHi.value - Constants.suckyDoubleError) ) &&
(nVelHi > (velLo.value + Constants.suckyDoubleError) ) &&
(nFreqLo < (freqHi.value - Constants.suckyDoubleError) ) &&
(nFreqHi > (freqLo.value + Constants.suckyDoubleError) )) {
return -1; // schneidet andere Zone
}
}
if( index >= 0 ) {
this.smps.insertElementAt( smp, index );
}
}
return index;
}
/**
* Entfernt eine SampleZone von der Karte
*
* @return false, wenn Zone nicht existiert
*/
public boolean removeSample( int index )
{
try {
smps.removeElementAt( index );
return true;
}
catch( IndexOutOfBoundsException e ) {
return false;
}
}
/**
* Besorgt eine Zone
*/
public SmpZone getSample( int index )
{
SmpZone smp = null;
try {
smp = (SmpZone) smps.elementAt( index );
}
catch( IndexOutOfBoundsException e ) {}
return smp;
}
/**
* Besorgt eine Aufzaehlung aller SampleZonen
*/
public Enumeration getSamples()
{
return smps.elements();
}
/**
* Ermittelt Anzahl der SampleZonen
*/
public int size()
{
return smps.size();
}
/**
* Index eines (fiktiven) Punktes erfragen
* DER X-WERT WIRD NICHT GERASTERT
*
* @return der Index ist interpoliert, d.h. der Punkt liegt zwischen
* Math.floor( result ) und Math.ceil( result )!
* Double.NEGATIVE_INFINITY: Punkt liegt links der Kurvengrenze;
* Double.POSITIVE_INFINITY: Punkt liegt rechts der Kurzengrenze;
*/
/* public double indexOf( double x )
{
int size = points.size();
DoublePoint pt1, pt2, pt3;
int index1, index2, index3;
if( size == 0 ) return Double.POSITIVE_INFINITY; // keine Punkte
pt1 = (DoublePoint) points.firstElement();
pt2 = (DoublePoint) points.lastElement();
if( pt1.x > x ) return Double.NEGATIVE_INFINITY; // zu klein
if( pt2.x < x ) return Double.POSITIVE_INFINITY; // zu gross
index1 = 0;
index2 = size - 1;
// Suchverfahren: Strecke immer halbieren und diejenige
// weiterverfolgen, in der das gesuchte X liegt
while( index1 + 1 < index2 ) {
index3 = (index1 + index2) / 2; // "floor"
pt3 = (DoublePoint) points.elementAt( index3 );
if( pt3.x > x ) {
pt2 = pt3;
index2 = index3;
} else {
pt1 = pt3;
index1 = index3;
}
}
if( pt1.x != pt2.x ) {
return( (double) index1 + (x - pt1.x) / (pt2.x - pt1.x) );
} else {
return( (double) index1 );
}
}
*/
/**
* Transformiert eine Kurve von einer Groesse/Masseinheit (zweidimensional)
* in eine andere (zweidimensional); vgl. auch Param.transform()!
*
* - bei gleichen ParamSpaces wird einfach src zurueckgeliefert.
* - ein ParamSpace.fitValue() wird auf jeden Punkt angewandt!
*
* @param hRef optionale (horizontale) Referenz, der ggf. fuer Umrechnungen von/in
* relative Werte benoetigt wird; kann null sein;
* WENN ER BENOETIGT WIRD, MUSS ER ENTWEDER IN DER FORM
* ABS_UNIT VORLIEGEN ODER (OHNE REFERENZ) IN DIESE UMWANDELBAR SEIN!
*
* @param vRef optionale vertikale Referenz
*
* @param stream optionaler Stream-Referrer, der ggf. fuer Umrechnungen
* von/in Beats oder Semitones benoetigt wird; darf null sein
* (ggf. wird dann auf die globale Geschwindigkeit/Scala
* zurueckgegriffen)
*/
/* public static SmpMap transform( SmpMap src, ParamSpace destHSpace, ParamSpace destVSpace,
Param hRef, Param vRef, SpectStream stream )
{
if( destHSpace.contains( src.hSpace ) &&
destVSpace.contains( src.vSpace )) return src;
SmpMap dest = new SmpMap( destHSpace, destVSpace, src.type );
DoublePoint pt;
Param newX, newY;
for( int i = 0; i < src.points.size(); i++ ) {
pt = (DoublePoint) src.points.elementAt( i );
newX = Param.transform( new Param( pt.x, src.hSpace.unit ), destHSpace.unit,
hRef, stream );
newY = Param.transform( new Param( pt.y, src.vSpace.unit ), destVSpace.unit,
vRef, stream );
if( (newX != null) && (newY != null) ) {
dest.addPoint( newX.value, newY.value );
}
}
return dest;
}
*/
// -------- StringComm methods --------
public String toString()
{
StringBuffer strBuf;
strBuf = new StringBuffer( hSpace.toString() + '|' + vSpace.toString() + '|' + type );
for( int i = 0; i < smps.size(); i++ ) {
strBuf.append( "|" + ((SmpZone) smps.elementAt( i )).toString() );
}
return( strBuf.toString() );
}
/**
* @param s MUST BE in the format as returned by SmpMap.toString()
*/
public static SmpMap valueOf( String s )
{
StringTokenizer strTok;
SmpMap map;
strTok = new StringTokenizer( s, "|" );
map = new SmpMap( ParamSpace.valueOf( strTok.nextToken() ), // hSpace
ParamSpace.valueOf( strTok.nextToken() ), // vSpace
Integer.parseInt( strTok.nextToken() )); // type
while( strTok.hasMoreElements() ) {
map.smps.addElement( SmpZone.valueOf( strTok.nextToken() ));
}
return map;
}
}
// class SmpMap