import javax.swing.JFrame;
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import java.util.*;
import jass.generators.*;
import jass.render.*;
import jass.engine.*;
/**
Divide window into four triangles defined by the 4 corners and the
middle. Assign modal models to the vertices in the order center
topleft bottomleft bottomright topright, and interpolate the models
in each triangle using barycentric coordinates. Labeling of
triangles and vertices is as follows: u[0],u[1] (x,y) are in range [0
- 1]
x -->
y 1 _____________ 4
| |\ /|
| | \ 0 / |
V | \ / |
| 1 0 3 |
| / \ |
| / 2 \ |
|/___________\|
2 3
Modal models are assigned on commandline (see makefile for "make run").
Input is provided through audio-in, a contact mike as a virtual drumstick
is effective.
*/
public class Pyramid implements MouseListener, MouseMotionListener, ComponentListener {
public static final int DIM = 2;
/** component over which mouse events are gathered */
Component component;
// scaled mouse position
float[] uCurrent = new float[] {0,0};
// scaled mouse click position
float[] uClick = new float[] {0,0};
/** dimension of component, in pixels. Changed if component is resized */
Dimension size;
/** Construct input source, which produces mouse (x,y) positions
* from dragging on <code>component</code>.
*
* @param component Active component on which to drag
*/
Pyramid(Component component) {
this.component = component;
size = component.getSize();
drawDisplay();
start();
}
/** Initialize resources and start capture.
Must call {@link #read} at <code>inputRate</code> to consume
input data.
@return true if successfully started.
*/
public boolean start() {
component.addMouseListener(this);
component.addMouseMotionListener(this);
component.addComponentListener(this);
return true;
}
/** Stop collecting data and free resources */
public void stop() {
component.removeMouseListener(this);
component.removeComponentListener(this);
}
/** read next input. A client of this class should arrange to
read the data out at the inputRate.
Returns the latest mouse position. */
public void read(float[] u,int x, int y) {
u[0] = (float) x / size.width ;
u[1] = (float) y / size.height ;
}
/** return input dimension */
public int getDimension() {
return DIM;
}
private void drawDisplay() {
}
////////////////////////////////////////
///Implement ComponentListener
public void componentResized(ComponentEvent e) {
size = component.getSize();
drawDisplay();
}
public void componentHidden(ComponentEvent e) {
drawDisplay();
}
public void componentMoved(ComponentEvent e) {
drawDisplay();
}
public void componentShown(ComponentEvent e) {
drawDisplay();
}
// implement MouseMotionListener
public void mouseDragged(MouseEvent e) {
}
public void mouseMoved(MouseEvent e) {
int x = e.getX();
int y = e.getY();
read(uCurrent,x,y);
//System.out.println(uCurrent[0]+","+uCurrent[1]);
mp.setPoint(uCurrent[0],uCurrent[1]);
}
// implement MouseListener
public void mouseClicked(MouseEvent e) {
int x = e.getX();
int y = e.getY();
read(uClick,x,y);
//System.out.println("("+uClick[0]+","+uClick[1]+")");
mp.setPoint(uClick[0],uClick[1]);
}
public void mouseEntered(MouseEvent e) {
}
public void mouseExited(MouseEvent e) {
}
public void mousePressed(MouseEvent e) {
}
public void mouseReleased(MouseEvent e) {
}
static SourcePlayer player;
static final int nvertices = 5; // must be 5
static ModalPyramid mp;
static OneShotBuffer force; // excitation
static AudioIn audioIn; // alternate excitation
public static void main (String[] args) {
float srate = 44100;
int bufferSize = 128;
int bufferSizeJavaSound = 8*1024;
if(args.length != 5) {
System.out.println("Usage: java Pyramid 1.sy 2.sy 3.sy 4.sy 5.sy");
return;
}
player = new SourcePlayer(bufferSize,bufferSizeJavaSound,srate);
ModalModel[] mm = new ModalModel[nvertices]; // a model for each vertex
try {
for(int i=0;i<nvertices;i++) {
mm[i] = new ModalModel(args[i]);
}
} catch (java.io.FileNotFoundException ee) {
System.out.println("A modes file was not found\n");
}
mp = new ModalPyramid(mm,srate,bufferSize);
audioIn = new AudioIn(srate,bufferSize,0);
try {
player.addSource(mp);
mp.addSource(audioIn);
} catch(SinkIsFullException ee) {
System.out.println(ee);
System.exit(0);
}
player.start();
//Create the top-level container and add viewer
JFrame frame = new JFrame() {
public void paint(Graphics g) {
int w = getWidth();
int h = getHeight();
g.clearRect(0,0,w,h);
g.drawLine(0,0,w,h);
g.drawLine(w,0,0,h);
}
};
//Finish setting up the frame, and show it.
frame.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
frame.setSize(new Dimension(400,400));
frame.setVisible(true);
Pyramid mi = new Pyramid(frame);
}
}
class ModalPyramid extends ModalObjectWithOneContact {
int maxModes = 1000000;
int nvertices = 5;
ModalModel mmCurrent;
ModalModel[] mmT = new ModalModel[3]; // at vertices of triangle
ModalModel[] allmm; // one for each vertex
public ModalPyramid(ModalModel[] mm,float srate,int bufferSize) {
super(mm[0],srate,bufferSize);
allmm = mm;
nvertices = mm.length;
for(int i=0;i<nvertices;i++) {
if(mm[i].nfUsed < maxModes) {
maxModes = mm[i].nfUsed;
}
}
System.out.println("maxModes="+maxModes);
// set number of modes to smalles occurring in all models
setNf(maxModes);
// create a modalmodel which will be recomputed all the time in setPoint
mmCurrent = new ModalModel(maxModes,1); // one location
this.modalModel = mmCurrent;
}
// tmap[t][k] is kth vertex of triangle t
int[][] tmap = {
{0,4,1},
{0,1,2},
{0,2,3},
{0,3,4}
};
// q[v][] is (x,y) vector of vth vertex
float[][] q = {
{.5f,.5f},
{0,0},
{0,1},
{1,1},
{1,0}
};
// compute from (x,y) in [0 1]X[0 1] the triangle and
// the barycentric coordinates of (x,y) in that triangle
public void setPoint(float x,float y) {
int t; // triangle index
float foo = x+y;
if(x > y) { // is 0 or 3
if(foo > 1) {
t = 3;
} else {
t = 0;
}
} else { // is 1 or 2
if(foo > 1) {
t = 2;
} else {
t = 1;
}
}
// have triangle t. Compute barycentric coordinates
// call (x,y) p. Let the 3 triangle vertices be q1 q2 q3,
// and the barycentric coordinates b1 b2 b3. p,q 2-vectors,
// b are scalars. 2 equations for b are:
// (q1-q3)b1+(q2-q3)b2 = p-q3
// b3 = 1-b1-b2
// define:
// r1 = q1-q3,
// r2=q2-q3,
// r3=p-q3,
// rr = (r1.r2)^2 - (r1.r1)*(r2.r2)
// then
// b1 = ((r1.r2)*r2-(r2.r2)*r1).r3 /rr
// b2 = ((r1.r2)*r1-(r1.r1)*r2).r3 /rr
// or, defining
// w1 = (r1.r2)*r2-(r2.r2)*r1)/rr
// w2 = (r1.r2)*r1-(r1.r1)*r2)/rr
// b1 = w1.r3
// b2 = w2.r3
// init r1 r2 r3 as q1 q2 q3
float[] r1 = {q[tmap[t][0]][0],q[tmap[t][0]][1]};
float[] r2 = {q[tmap[t][1]][0],q[tmap[t][1]][1]};
float[] r3 = {q[tmap[t][2]][0],q[tmap[t][2]][1]};
/*
System.out.println("q1= "+r1[0] +" "+r1[1]);
System.out.println("q2= "+r2[0] +" "+r2[1]);
System.out.println("q3= "+r3[0] +" "+r3[1]);
*/
r1[0] = r1[0] - r3[0];
r1[1] = r1[1] - r3[1];
r2[0] = r2[0] - r3[0];
r2[1] = r2[1] - r3[1];
r3[0] = x - r3[0];
r3[1] = y - r3[1];
// got r's now
// inner products
float r11 = r1[0]*r1[0] + r1[1]*r1[1];
float r22 = r2[0]*r2[0] + r2[1]*r2[1];
float r12 = r1[0]*r2[0] + r1[1]*r2[1];
float rr = r12*r12 - r11*r22;
float[] w1 = {(r12*r2[0]-r22*r1[0])/rr,(r12*r2[1]-r22*r1[1])/rr};
float[] w2 = {(r12*r1[0]-r11*r2[0])/rr,(r12*r1[1]-r11*r2[1])/rr};
float[] b = {
w1[0]*r3[0] + w1[1]*r3[1],
w2[0]*r3[0] + w2[1]*r3[1],
0
};
b[2] = 1 - b[0] - b[1];
// got barycentric coordinates.
// now load mmT with the correct 3 models corresponding to the vertices
mmT[0] = allmm[tmap[t][0]];
mmT[1] = allmm[tmap[t][1]];
mmT[2] = allmm[tmap[t][2]];
setModel(b);
//System.out.println("t="+t+ " b= "+b[0] +" "+b[1]+" "+b[2]);
}
private void setModel(float[] b) {
for(int i=0;i< maxModes;i++) {
mmCurrent.f[i] = (float)(b[0]*mmT[0].f[i]+b[1]*mmT[1].f[i]+b[2]*mmT[2].f[i]);
mmCurrent.d[i] = (float)(b[0]*mmT[0].d[i]+b[1]*mmT[1].d[i]+b[2]*mmT[2].d[i]);
mmCurrent.a[0][i] = (float)(b[0]*mmT[0].a[0][i]+b[1]*mmT[1].a[0][i]+b[2]*mmT[2].a[0][i]);
}
this.computeFilter();
}
}