diff options
-rw-r--r-- | SNPGUI.sc | 323 |
1 files changed, 215 insertions, 108 deletions
@@ -2,18 +2,25 @@ SNPGUI { var <speakerSetup;//Array holding the chromosome numbers for each speaker var <mainView;// main view object + var <speakerView;// main view object var <drawView;// main view object var <speakerDict;//Dictionary of Views (per speaker) var <textDict;//Dictionary of StaticText obbjects - var <taskSNP;//task for updating the SNP texts of chromosomes - var <snpDict; //Dictionary of texts for SNPs on chromosomes (replaceable) + var <updateSNPTextTask;//task for updating the SNP texts of chromosomes + var <snpTextFadeTasks; //Dictionary of tasks for SNP texts + var <>textFadeTime = 0.5; // time (in seconds) it takes for a SNP text to fade out + var <>textFadeSteps = 20; // amount of steps within textFadeTime in which to blend the color of a SNP text + var <>speakerViewMargin = 4; // the margin between speakerView border and a text within it + var <>speakerViewHeadMargin = 24; // the margin reserved for the speaker name + var <intermediateTextDict; //Dictionary of texts for SNPs on chromosomes (intermediate) var <arcRadius = 200;//size of the arc, surrounded by speakers var <radiusOffset = 75;//size of the arc, surrounded by speakers var <mainWidth = 800; var <mainHeight = 800; - var <speakerHeight = 50; - var <speakerWidth = 50; - var <lineHeight = 12; + var <speakerHeight = 70; + var <speakerWidth = 70; + var <lineHeight = 16; + var <defaultFont = "Monaco"; *new{ arg setup; @@ -22,154 +29,254 @@ SNPGUI { snpGUIInit{ arg setup; - setup.size.postln; speakerSetup = setup; - this.setupViews(setup.size); - //create a dictionary the size of the setup + this.createDicts; + this.createViews; + this.createTasks; } + // initialize Dictionaries for all things - initDicts{ - arg setup; - speakerDict = Dictionary.new(setup.size+1); - //textDict = Dictionary.new(setup.size); - textDict = Dictionary.new(25);//Dictionary holding all StaticText objects that can be modified - snpDict = Dictionary.new(25); - //taskDict = Dictionary.new(25); - //add each speaker number - setup.do({|speaker,i| - var tmp = Dictionary.new(setup[0].size); + createDicts{ + speakerDict = Dictionary.new(speakerSetup.size+1); + intermediateTextDict = Dictionary.new(SNPInfo.chromosomesLength.size.asInt); // Dictionary of intermediate Strings to be used for the StaticText objects in textDict + textDict = Dictionary.new(SNPInfo.chromosomesLength.size.asInt);//Dictionary holding all StaticText objects that can be modified + } + + // creates all Views + createViews{ + this.createMainView; +// this.createDrawView; + this.createSpeakerViews; + } + + // create the main View (the main window) + createMainView{ + mainView = View.new(); + mainView.minSize = mainWidth@mainHeight; + mainView.userCanClose = true; + mainView.enabled = true; + mainView.visible = true; + mainView.name = "The Sound Of People"; + mainView.background = Color.fromHexString("#DBDBDB"); + mainView.onResize = { + if((drawView.notNil), { + drawView.moveTo((mainView.absoluteBounds.width/2)-(drawView.bounds.width/2), (mainView.absoluteBounds.height/2)-(drawView.bounds.height/2)); + }); + speakerView.moveTo((mainView.absoluteBounds.width/2)-(speakerView.bounds.width/2), (mainView.absoluteBounds.height/2)-(speakerView.bounds.height/2)); + }; + } + + //creates a UserView, draws a circle + createDrawView{ + drawView = UserView.new(mainView, Rect(0, 0, mainView.bounds.width, mainView.bounds.height)); + drawView.minSize = mainWidth@mainHeight; + drawView.background = Color.fromHexString("#DBDBDB"); + drawView.background = Color.fromHexString("#e7ebe7"); + // center to mid of mainView on resize + drawView.onResize = { drawView.moveTo(mainView.bounds.width/2-drawView.bounds.width/2, mainView.bounds.height/2-drawView.bounds.height/2)}; + // draw a circle + drawView.drawFunc = { + Pen.translate(drawView.bounds.width/2, drawView.bounds.height/2); + Pen.strokeColor = Color.black; + Pen.color = Color.black; + Pen.addArc(0@0, arcRadius, 0, 2pi); + Pen.stroke; + }; + } + + // creates all speakerViews necessary for the setup defined in speakerSetup + createSpeakerViews{ + speakerView = View.new( + mainView, + Rect( + 0, + 0, + mainWidth, + mainHeight + ) + ); + speakerView.minSize = mainWidth@mainHeight; + // create a speakerView for each speaker in the setup + speakerSetup.do({|speaker,i| // Adds a View with speaker information to the speakerDict - speakerDict.add((i+1).asSymbol -> this.initSpeakerView(i, setup.size, setup[0].size)); + speakerDict.add((i+1).asSymbol -> this.createSpeakerView(i, speakerSetup.size, speakerSetup[0].size)); // iterate all chromosome information from original setup speaker.do({|chromosome, j| //add chromosome number -> replaceable SNP StaticText object to temporary Dictionary - //tmp.add(chromosome.asSymbol -> this.initChromosomeText(chromosome.asSymbol, (i+1).asSymbol, j+1)); - textDict.add(chromosome.asSymbol -> this.initChromosomeText(chromosome.asSymbol, (i+1).asSymbol, j+1)); - snpDict.add(chromosome.asSymbol -> ""); + textDict.add(chromosome.asSymbol -> this.createChromosomeTexts(chromosome.asSymbol, (i+1).asSymbol, j)); + intermediateTextDict.add(chromosome.asSymbol -> ""); }); - //textDict.add((i+1).asSymbol -> tmp); }); // Adds a View for MT chromosome with speaker information to the speakerDict - speakerDict.add(\all -> this.initSpeakerView(-1, setup.size, 1)); + speakerDict.add(\all -> this.createSpeakerView(-1, speakerSetup.size, 1)); // add chromosome text for MT to the speaker called all - textDict.add(\25 -> this.initChromosomeText(\25, \all, 1)); - // - snpDict.add(25.asSymbol -> ""); - //start a Task to update the SNP currently playing - this.startSNPTask; - //snpDict.postln; - //speakerDict.postln; - //textDict.postln; - } - - //init a StaticText for a chromosome in its speaker View, returns its replaceable component (for SNPs) - initChromosomeText{ - arg chromosome, number, position; - var snpText, chromosomeText; - chromosomeText = StaticText.new(speakerDict.at(number), Rect(0, lineHeight*position, (speakerDict.at(number).bounds.width/2), lineHeight)); - chromosomeText.string = (chromosome.asString++": "); - chromosomeText.align = \left; - snpText = StaticText.new(speakerDict.at(number), Rect((speakerDict.at(number).bounds.width/2), lineHeight*position, (speakerDict.at(number).bounds.width/2), lineHeight)); - snpText.string = "Test"; - snpText.align = \right; - ^snpText; + textDict.add(SNPInfo.chromosomesLength.size.asSymbol -> this.createChromosomeTexts(SNPInfo.chromosomesLength.size.asSymbol, \all, 0)); + intermediateTextDict.add(SNPInfo.chromosomesLength.size.asSymbol -> ""); } - //inits a View for one speaker, adds its name as StaticText object and returns itself - initSpeakerView{ - arg number, total, perSpeaker; - var mid, radius, radianOffset, radian, x, y, speakerContainer, text; - mid = Point.new(mainWidth/2,mainHeight/2);//mid of the circle + //creates a View for one speaker, adds its name as StaticText object and returns itself + createSpeakerView{ + arg number, total, chromosomesOnSpeaker; + var mid, radius, radianOffset, radian, x, y, speakerContainer, speakerContainerInner, speakerText; + mid = Point.new((mainWidth/2)-(speakerWidth/2),(mainHeight/2)-(speakerHeight/2));//mid of the circle radius = arcRadius+radiusOffset;//make a slightly bigger radius for the text objects - radianOffset = 360/total*2;// radian offset used to make speaker 1 be positioned front left - radian = 360/total*number;//segment of the circle the speaker is placed in + radianOffset = 360/(total*2);// radian offset used to make speaker 1 be positioned front left + radian = (360*(number/total))-90;//segment of the circle the speaker is placed in //Calculating x and y coordinates for the View to be created x = mid.x+(radius*cos(degrad(radian-radianOffset))); y = mid.y+(radius*sin(degrad(radian-radianOffset))); - //setting up the View container for a speaker - speakerContainer = View.new(drawView, Rect(x, y, speakerWidth, (perSpeaker+1)*lineHeight)); + //setting up the border for a speaker + speakerContainer = View.new( + speakerView, + Rect( + x, + y, + speakerWidth, + ((chromosomesOnSpeaker)*lineHeight)+(speakerViewMargin*2)+speakerViewHeadMargin + ) + ); speakerContainer.visible = true; - speakerContainer.background = Color.green; + speakerContainer.background = Color.fromHexString("#7f7f7f"); + //setting up the View container for a speaker + speakerContainerInner = View.new( + speakerContainer, + Rect( + (speakerViewMargin/2), + (speakerViewMargin/2), + speakerWidth-speakerViewMargin, + ((chromosomesOnSpeaker)*lineHeight)+speakerViewMargin+speakerViewHeadMargin + ) + ); + speakerContainerInner.visible = true; + speakerContainerInner.background = Color.fromHexString("#EEEEEE"); - // adding the speaker number - text = StaticText(speakerContainer, Rect(0, 0, speakerContainer.bounds.width, lineHeight)); - text.string = (number+1).asString; - text.align = \center; + // add the speaker text (number of the speaker) + speakerText = StaticText( + speakerContainerInner, + Rect( + 0, + speakerViewMargin, + speakerContainer.bounds.width, + lineHeight + ) + ); + speakerText.align = \center; + speakerText.string = (number+1).asString; + speakerText.stringColor = Color.fromHexString("#4A52C4"); + speakerText.font = Font(this.defaultFont,16,true); //add exception for no/ all speaker if(number<0,{ + // rename the speaker + speakerText.string = "all"; //recalculate radius and offset radius = radius+radiusOffset; - radian = 360/total*0; - //Calculating x and y coordinates for the View to be created + radian = -90+((360/total)/2); + //recalculate x and y coordinates to move the View x = mid.x+(radius*cos(degrad(radian-radianOffset))); y = mid.y+(radius*sin(degrad(radian-radianOffset))); + // move speakerContainer to new coordinates (top center) speakerContainer.moveTo(x, y); - text.string = "all"; }); -// ("Speaker: "++number.asString).postln; -// ("cos("++radian.asString++"): "++cos(degrad(radian)).asString).postln; -// ("sin("++radian.asString++"): "++sin(degrad(radian)).asString).postln; ^speakerContainer; } - /* - * Setup the initial window and all of its StaticText elements - */ - setupViews{ - arg numChannels; - mainView = View.new(nil,Rect(50,50,mainWidth,mainHeight)); - //mainView.front; - mainView.fixedSize=mainWidth@mainHeight; - mainView.enabled = true; - mainView.userCanClose = true; - mainView.alwaysOnTop; - mainView.visible = true; - mainView.name = "The Sound Of People"; - mainView.background = Color.white; - mainView.bounds.postln; - //make a UserView for drawing with Pen - drawView = UserView.new(mainView, Rect(0, 0, mainView.bounds.width, mainView.bounds.height)); - drawView.animate = true; - drawView.frameRate = 60.0; - // draw a circle - drawView.drawFunc = { - Pen.translate(drawView.bounds.width/2, drawView.bounds.height/2); - Pen.strokeColor = Color.black; - Pen.color = Color.black; - Pen.addArc(0@0, arcRadius, 0, 2pi); - Pen.stroke; - }; - //add StaticText objects for names of speakers, names of chromosomes and names of SNPs - this.initDicts(speakerSetup); + //create a StaticText for a chromosome in its speaker View, returns its replaceable component (for SNPs) + createChromosomeTexts{ + arg chromosome, number, position; + var snpText, chromosomeText; + //create the name of the chromosome as StaticText + chromosomeText = StaticText.new( + speakerDict.at(number), + Rect( + speakerViewMargin, + (lineHeight*position)+speakerViewHeadMargin, + (speakerDict.at(number).bounds.width/2), + lineHeight + ) + ); + chromosomeText.align = \left; + chromosomeText.string = if((chromosome.asInt < 10), {("0"++chromosome.asString++": ")}, {chromosome.asString++": "}); + chromosomeText.font = Font(this.defaultFont,13,true,true); + + //create the snpText (its string will be set with a Task later) + snpText = StaticText.new( + speakerDict.at(number), + Rect(( + speakerDict.at(number).bounds.width/2)-speakerViewMargin, + (lineHeight*position)+speakerViewHeadMargin, + (speakerDict.at(number).bounds.width/2), + lineHeight + ) + ); + snpText.align = \right; + snpText.string = ""; + snpText.font = Font(this.defaultFont,13,true); + ^snpText; } + + //create all Tasks + createTasks{ + this.createSNPTextFadeTasks(SNPInfo.chromosomesLength.size.asInt); + this.createUpdateSNPTextTask; + } + + // create Tasks to fade out the text of all SNPs and blend its color gradually + createSNPTextFadeTasks{|size| + snpTextFadeTasks = Dictionary.new(size); + size.do({|item,i| + var textTask; + snpTextFadeTasks.put((i+1).asSymbol, textTask = Task({ + var snp = (i+1).asSymbol; + var elapsed = 0.0; + // reset the intermediateTextDict text + this.setIntermediateText(snp, ""); + // blend the original color of the chromosome's SNP by the amount of steps defined in textFadeSteps with black. Each step's length is defined by textFadeTime + this.textFadeSteps.do({|item,i| + textDict.at(snp.asSymbol).alpha_(textDict.at(snp.asSymbol).alpha-(textFadeTime/this.textFadeSteps)); + textDict.at(snp.asSymbol).stringColor_(textDict.at(snp.asSymbol).stringColor.blend(Color.black(), textFadeTime/this.textFadeSteps)); + (textFadeTime/this.textFadeSteps).wait; + }); + textDict.at(snp.asSymbol).string_(""); + }) + ); + }); + } + //Starts a task to update texts for SNP of chromosomes on AppClock - startSNPTask{ - taskSNP = Task.new({ + createUpdateSNPTextTask{ + updateSNPTextTask = Task.new({ { //get all values from the dictionary and update the corresponding StaticText objects - snpDict.keysValuesDo({|key, value| - textDict.at(key.asSymbol).string_(value.asString); + intermediateTextDict.keysValuesDo({|key, value| + if((value.asString != ""),{ + //set the text and color for the SNP here,as doing this within the fade Task would delay it too much + textDict.at(key.asSymbol).string_(value.asString); + // set different color for each base/ combination of bases + textDict.at(key.asSymbol).stringColor_(Color.fromHexString(SNPInfo.getColorFromBase(value.asString))); + //start or reset the snpTextTask belonging to this chromosome (to fade out its text) + if((snpTextFadeTasks.at(key.asSymbol).isPlaying), { + snpTextFadeTasks.at(key.asSymbol).reset; + },{ + snpTextFadeTasks.at(key.asSymbol).start(AppClock); + }); + }); }); 0.02.wait; }.loop; }); - taskSNP.play(AppClock); + updateSNPTextTask.play(AppClock); } //set the text of a specific chromosome (aka.: which SNP is currently playing?) - setSNPText{ + setIntermediateText{ arg chromosome, text; - //delete all strings first -// snpDict.keysValuesChange{|key,value| -// ^""; -// }; - //add the current SNP text to the dictionary - snpDict.put(chromosome.asSymbol, text.asString); + intermediateTextDict.put(chromosome.asSymbol, text.asString); } + // get the current SNP text of a chromosome - getSNPText{ + getIntermediateText{ arg chromosome; - var text = snpDict.at(chromosome.asSymbol); + var text = intermediateTextDict.at(chromosome.asSymbol); ^text; } } |