How to Make a PowerPoint-like GUI?

Discussions on everything related to the software, electronic, and mechanical components of information systems and instruments.

How to Make a PowerPoint-like GUI?

Postby Natural ChemE on March 19th, 2012, 3:37 pm 

I’m working on a new CAD, and the first part that I’d like to tackle is the GUI. For funding reasons, it’s easier to have a shiny GUI and say “and, yeah, this function will work later once the engine is done” than it is to say “and this engine’s great, but you can't see it because the GUI hasn't been made yet”.

Making the forms, text boxes, menus, etc. is all really simple in C#. In fact it’s pretty much all down except for the hard part: the graphics area where users drag-and-drop unit operations, connect streams between the unit operations, etc. In this model the unit operations are pretty much like clip art or text boxes where they’ll have a body that can be translated and corners that can be dragged to resize them.

The issue here is that I don’t know how to do any of this. I made a program about a year ago that drew chemical structures (there’s a thread on it somewhere on this board), but it wasn’t very smooth despite the chemical structures being just different collections of text with lines drawn between them. And by “wasn’t very smooth”, I mean that when the user dragged or resized the chemical, you could see the graphics redrawing themselves; it looked unprofessionally laggy.

My main concern is that, if I repeat that methodology, then it’d look even more laggy since these new graphics will actually be icon-like instead of merely being text with lines. And while that’s not a fatal issue for a proof-of-concept GUI, I’d like to do it closer to right the first time through.

So, how can I make an efficient drag-and-drop-objects sort of GUI? Can it even be done in C#, or should I just use C++?

Side point: I know very little about how a computer handles graphics. I get the concept of the GPU and such, it’s just that I don’t get how code becomes implemented on the GPU, or how what I program becomes GPU-based code instead of CPU-based code. The abstraction layers are black boxes to me here.
Natural ChemE
Forum Moderator
 
Posts: 1390
Joined: 28 Dec 2009
Location: Existential Desperation, VA
Blog: View Blog (0)


Re: How to Make a PowerPoint-like GUI?

Postby CanadysPeak on March 19th, 2012, 5:20 pm 

Natural ChemE wrote:I’m working on a new CAD, and the first part that I’d like to tackle is the GUI. For funding reasons, it’s easier to have a shiny GUI and say “and, yeah, this function will work later once the engine is done” than it is to say “and this engine’s great, but you can't see it because the GUI hasn't been made yet”.

Making the forms, text boxes, menus, etc. is all really simple in C#. In fact it’s pretty much all down except for the hard part: the graphics area where users drag-and-drop unit operations, connect streams between the unit operations, etc. In this model the unit operations are pretty much like clip art or text boxes where they’ll have a body that can be translated and corners that can be dragged to resize them.

The issue here is that I don’t know how to do any of this. I made a program about a year ago that drew chemical structures (there’s a thread on it somewhere on this board), but it wasn’t very smooth despite the chemical structures being just different collections of text with lines drawn between them. And by “wasn’t very smooth”, I mean that when the user dragged or resized the chemical, you could see the graphics redrawing themselves; it looked unprofessionally laggy.

My main concern is that, if I repeat that methodology, then it’d look even more laggy since these new graphics will actually be icon-like instead of merely being text with lines. And while that’s not a fatal issue for a proof-of-concept GUI, I’d like to do it closer to right the first time through.

So, how can I make an efficient drag-and-drop-objects sort of GUI? Can it even be done in C#, or should I just use C++?

Side point: I know very little about how a computer handles graphics. I get the concept of the GPU and such, it’s just that I don’t get how code becomes implemented on the GPU, or how what I program becomes GPU-based code instead of CPU-based code. The abstraction layers are black boxes to me here.


Don't take this as a snarky question, but why are you making a CAD? It's all been done quite well. Unless, of course, you're doing a 4D one - that would be fun.
User avatar
CanadysPeak
Resident Member
 
Posts: 4670
Joined: 31 Dec 2008
Location: Pittsburgh
Blog: View Blog (0)


Re: How to Make a PowerPoint-like GUI?

Postby Natural ChemE on March 19th, 2012, 7:21 pm 

CanadysPeak wrote:[..]why are you making a CAD? It's all been done quite well.


I first started seriously considering it after seeing how annoyed the folks in my field are at the current options. Unlike the more robust offerings like those from Autodesk, process engineering CAD's just aren't up to spec.

I've recently travelled to meet with the folks who make the top package, AspenONE, and appealed to them to make a list of improvements that I had provided. They offered me a job, but basically said that they couldn't implement the features that I had requested. Even the simple bug fixes. And that's after I talked to at least six of their Vice Presidents (it feels like half of the employees there are a Vice President of something or another).

After dealing with that, I started talking to some of the other grads about it. We came up with a bunch of really cool ideas for it. Ideas that would be amazing if done well.

So, if we succeed, then we'll be in a position to have a grossly profitable product. If we fail, well, it'll have been a really great learning experience and a project to tack on the resume.
Natural ChemE
Forum Moderator
 
Posts: 1390
Joined: 28 Dec 2009
Location: Existential Desperation, VA
Blog: View Blog (0)


Re: How to Make a PowerPoint-like GUI?

Postby kidjan on April 1st, 2012, 5:30 pm 

Natural ChemE wrote:The issue here is that I don’t know how to do any of this. I made a program about a year ago that drew chemical structures (there’s a thread on it somewhere on this board), but it wasn’t very smooth despite the chemical structures being just different collections of text with lines drawn between them. And by “wasn’t very smooth”, I mean that when the user dragged or resized the chemical, you could see the graphics redrawing themselves; it looked unprofessionally laggy.

...

So, how can I make an efficient drag-and-drop-objects sort of GUI? Can it even be done in C#, or should I just use C++?


It's less of a language issue and more implementation; getting graphics to work smoothly usually takes a lot of hard work. Using C++ certainly won't fix dragging and dropping. If anything, it may actually be more difficult.

Could you post the code you were using to do drag and drop before?

Side point: I know very little about how a computer handles graphics. I get the concept of the GPU and such, it’s just that I don’t get how code becomes implemented on the GPU, or how what I program becomes GPU-based code instead of CPU-based code. The abstraction layers are black boxes to me here.


Frankly, you shouldn't want or need to know this. I think you should expect a higher-level API that abstracts the details out for you.
User avatar
kidjan
Active Member
 
Posts: 1926
Joined: 25 Jul 2007
Location: Earth.
Blog: View Blog (0)


Re: How to Make a PowerPoint-like GUI?

Postby Natural ChemE on April 10th, 2012, 9:44 pm 

kidjan,

Thank you for your reply; sorry that mine's been so delayed.

First and last time I'd touched graphics (besides ASCII art-like graphs and such) was back when I did that chemical-drawing-program thingie. The old thread was Best way to draw chemicals in C#. Since I don't have the code for it on this computer, I'll have to resort to just copy/pasting what I had there:
Code: Select all
m_objDrawingSurface = new Bitmap(100, 100, System.Drawing.Imaging.PixelFormat.Format24bppRgb);

Then if memory serves, there were DrawLine() and DrawText()-like commands which I used to draw onto that Bitmap. I then connected the drawing routine to a MouseDown() event (or something) so that, when the user dragged their mouse, it'd repaint the chemical in the new position. Also had a slider that resized it and another that rotated it, both using their own OnChange() or something events to trigger the redrawing.

Since I made this post, I got a copy of Aspen's newest version, V7.3.2. It's actually quite funny given my situation. They remade their own GUI in .NET 4.0 (presumably with VB.NET). Their new interface is one of the laggiest things that I've ever had the displeasure of needing to use for a long period of time, yet the new features are undeniably useful.

Of relevance, they apparently hooked up their old drawing area to the .NET GUI. It's obviously the same control, and it's not laggy at all. Not sure if they did this for simple convenience (i.e., they already had made that part, why remake it?) or if they had a similar concern about .NET's laggier nature. (Side question: How would they have best hooked up their own control? Using ActiveX, COM, or what?)

I haven't done much with the GUI. I know that it was my first step to tackle, but I've been having fun with the engine (which I consider to be the novel, fun part). This whole project took a huge leap forward when I realized that Aspen has an ActiveX server that allows me to call current simulations, alter them, run them, etc. While it's entirely my plan to eventually have this new CAD be all new code, I've been putting together wrappers such that I can call the current features (i.e., unit operations and physical property database) from my own flowsheet-level convergence scheme. Then I can iteratively replace 'em while still retaining a fully working CAD.

The fun in this has been what I've been able to improve. I.e., knocking out common bugs (that Aspen inexplicably ignores) and using distributed computation.

Anyway, rant aside, I'd still like to figure out the whole graphics thing. As a rule of thumb I tend to prefer low-level. It's just easier for me to write my own methods when I need 'em than rely on others'. I'm much better at analytical thought than memorization, so I tend to struggle much more with searching through documentation than I do just creating my own stuff from scratch. 'course I do like the hardware abstraction that tends to come with higher level work since it helps mitigate having to rewrite for new boxes (especially since I plan for this thing to be a massively distributed application in its ultimate form).

I also figure that low-level's the way to go since that's what we write stuff like games in. I mean, something like Crysis running on a virtual machine language (.NET, Java, etc.) just wouldn't be viable, would it? I've always heard that games tend to run off custom-made low-level graphics engines tailored to that sorta game.

Anyway, is there a way that I can just paint on a pixel-by-pixel basis efficiently? I haven't had much time to search, but the best article that I've found so far is Developing a GUI Using C++ and DirectX Part 1.
Natural ChemE
Forum Moderator
 
Posts: 1390
Joined: 28 Dec 2009
Location: Existential Desperation, VA
Blog: View Blog (0)


Re: How to Make a PowerPoint-like GUI?

Postby kidjan on April 14th, 2012, 12:44 am 

Natural ChemE wrote:kidjan,

Thank you for your reply; sorry that mine's been so delayed.

First and last time I'd touched graphics (besides ASCII art-like graphs and such) was back when I did that chemical-drawing-program thingie. The old thread was Best way to draw chemicals in C#. Since I don't have the code for it on this computer, I'll have to resort to just copy/pasting what I had there:
Code: Select all
m_objDrawingSurface = new Bitmap(100, 100, System.Drawing.Imaging.PixelFormat.Format24bppRgb);

Then if memory serves, there were DrawLine() and DrawText()-like commands which I used to draw onto that Bitmap. I then connected the drawing routine to a MouseDown() event (or something) so that, when the user dragged their mouse, it'd repaint the chemical in the new position. Also had a slider that resized it and another that rotated it, both using their own OnChange() or something events to trigger the redrawing.


I'd bet the barn the sluggishness has more to do with improper threading than C#; one of the issues with C# is it makes it so easy to put together apps that a lot of programmers forget you still need to know something about Windows Forms if you want to have a responsive UI.

In particular, there are strict rules about threading and UI controls; in practice, most winforms apps end up having a single thread on which all UI activity happens (we say it all gets "serialized" to the UI thread). This is a good thing, but it also means you need to be careful of doing long-running operations on the UI thread.

For example, every millisecond you block on the UI thread is a millisecond that the UI cannot respond to other actions; stuff will be queued up on the UI thread, resulting in sluggishness or jerkiness. This is, unfortunately, all too common for many winforms applications. It's easy to ignore if all the work you do happens quickly, but for really good performance work needs to happen on background threads, and then the results need to be serialized to the UI thread for rendering.

For rendering (either video or graphics), there are many, many other factors in play for getting smooth, consistent rendering that are totally independent of language. In particular, it's easy to do dumb things and get terrible performance with System.Drawing (you need to profile your code), you probably need double buffering to avoid flicker, and there isn't a language in the world that can make shoddy algorithms fast (bubble sort sucks in any language you choose; the solution isn't "use C++!", it's "stop using bubble sort!").


Of relevance, they apparently hooked up their old drawing area to the .NET GUI. It's obviously the same control, and it's not laggy at all. Not sure if they did this for simple convenience (i.e., they already had made that part, why remake it?) or if they had a similar concern about .NET's laggier nature. (Side question: How would they have best hooked up their own control? Using ActiveX, COM, or what?)


I doubt it has anything to do with .NET, and everything to do with their programmers not having the foggiest idea how windows forms programming works. And chances are their old control is smooth because it's been refined--not because it's written in native code.

For their own control, it's probably either a COM object or an ActiveX control; either is fairly easy to use inside .NET.

I also figure that low-level's the way to go since that's what we write stuff like games in. I mean, something like Crysis running on a virtual machine language (.NET, Java, etc.) just wouldn't be viable, would it? I've always heard that games tend to run off custom-made low-level graphics engines tailored to that sorta game.


Most games aren't written purely in .NET, no, but plenty of games use stuff like XNA, which is a managed runtime environment. And yes, I think writing games in .NET or java is totally viable. After all, most of the acceleration comes from native, high-level APIs like OpenGL or DirectX--which you can call from nearly any language. The performance doesn't come from "using native code."

If you want to go low-level, go low-level. Sounds like that's what you want to do, so....go do it. But I wouldn't fool yourself into thinking simply swapping languages is going to solve your performance problems, or going "low level" will suddenly make everything better. It's a pretty common misconception in the field, frankly.
User avatar
kidjan
Active Member
 
Posts: 1926
Joined: 25 Jul 2007
Location: Earth.
Blog: View Blog (0)


Re: How to Make a PowerPoint-like GUI?

Postby Natural ChemE on April 14th, 2012, 12:24 pm 

kidjan wrote:I'd bet the barn the sluggishness has more to do with improper threading than C#; one of the issues with C# is it makes it so easy to put together apps that a lot of programmers forget you still need to know something about Windows Forms if you want to have a responsive UI.

I can’t believe that you’d even suggest that I could make a mistake like that. Incidentally I know this guy, uh, Bob. And I think that he may’ve done improper threading, so, uh, I’ll give him your most excellent advice.

Seriously though, thanks. I suspect that you nailed the problem on the head.

kidjan wrote:Most games aren't written purely in .NET, no, but plenty of games use stuff like XNA, which is a managed runtime environment. And yes, I think writing games in .NET or java is totally viable. After all, most of the acceleration comes from native, high-level APIs like OpenGL or DirectX--which you can call from nearly any language. The performance doesn't come from "using native code."

If you want to go low-level, go low-level. Sounds like that's what you want to do, so....go do it. But I wouldn't fool yourself into thinking simply swapping languages is going to solve your performance problems, or going "low level" will suddenly make everything better. It's a pretty common misconception in the field, frankly.

If it is a misconception, then you’re right, it’s certainly a popular one. So much so that, from my asking around, I was led to believe that this was the consensus.

In general I’m having to take a lot of things on authority. And it’s giving me a lot of conflicting points of view. For instance I had an Aerospace professor insist that FORTRAN programs would always run at least twice as fast as C/C++ programs. Others have told me that there's simply no truth in that. But what am I to say? That professor has spent most of his professional life doing this, while others that contradicted him (and others that agreed) have also spent more years in such careers than I've been alive for.

Anyway, if you’re reasonably sure about .NET being fine for the graphics, I’ll try it again for this project. Once I’m done I’ll share the code for it – and hopefully be able to report that it works well.

So, last question. If I use C# 4.5 on Visual Studio 2012 (may as well design for the future, right?), probably using WPF, what general approach would you recommend for the graphics area? i.e., what class should I use as a drawing board, what functions should I use to draw my objects, and, if you happen to have the time to make such a recommendation, where should I think about putting new thread calls?
Natural ChemE
Forum Moderator
 
Posts: 1390
Joined: 28 Dec 2009
Location: Existential Desperation, VA
Blog: View Blog (0)


Re: How to Make a PowerPoint-like GUI?

Postby kidjan on April 16th, 2012, 10:22 pm 

Natural ChemE wrote:If it is a misconception, then you’re right, it’s certainly a popular one. So much so that, from my asking around, I was led to believe that this was the consensus.


There's probably lots of programmers and computer scientists who would argue otherwise, and I don't exactly have "proof" that what I'm saying is true. But personally, I think debates over "language x performance compared to language y" are generally rubbish.

Example: with a sufficient number of elements (i.e. more "n" in big-O notion), binary search will always outperform linear search on an ordered collection. I don't care if the linear search implementation is GPU accelerated, written in hand-tuned assembler and blessed by a voodoo witch doctor and Sarah Palin: algorithms dictate your final performance; not language. Anyone who says otherwise doesn't understand that O(n * 100) is still O(n).

For instance I had an Aerospace professor insist that FORTRAN programs would always run at least twice as fast as C/C++ programs


...to which your response should be all he's talking about is a paltry two-fold increase in performance. Example: binary search on an ordered collection takes O(log2(n)) operations, whereas linear search takes O(n). Want concrete numbers? Binary search is 30 operations for a billion items. Linear search is a billion operations for a billion items. So you can halve that billion by rewriting your linear search program in Fortran, or you can reduce it to ~30 operations by rewriting it as a binary search.

And yeah, you could halve the time for those 30 operations by going Fortran, but you have to ask yourself if it even matters at that point (it does not).

Others have told me that there's simply no truth in that.


There is some truth to it, although not the way he stated it. But even he told you something definitely true ("a well written program compiled with Fortran Intel is generally 20% faster than a comparable program compiled with GCC C," for example), there's a lot of caveats there.

First, you have to be using a specific version of Fortran; a "language" inherently has no performance; an implementation--a specific compiler, like Fortran Intel--has performance. Example: Mono is an open source implementation of .NET, and you can easily compare the performance of Mono to Microsoft's implementation of .NET. Another good example is python; pypy is a compiler implementation that generally outperforms cpython, another implementation of the language. There are probably dozens of compilers for C and C++, all of which have hugely different performance.

Second, the program being run matters. For example, on some programs--and in some environments--it's not uncommon for managed languages (e.g. MSFT .NET) to outperform traditional programs (e.g. GCC C). This isn't to say the exception is the rule--if you measure enough programs, you're going to get a clear idea of "general" performance of a given compiler--but if your program is an outlier, then that isn't particularly reassuring.

Third, a poorly written program is a bogus benchmark. In other words, if your Fortran-fanboi aerospace professor writes linear search in GCC C and Intel Fortran, and then benchmarks the two, he's probably going to proudly exclaim that his Fortran implementation is "twice as fast!" But it wouldn't take a rocket scientist (ha ha) to rewrite the program using binary search in, say, Perl, which would easily be thousands or millions of times faster for reasonably sized data sets. Another variant of this is testing a shitty implementation of some program in language x and a good implementation of the program in language y, and then concluding "language y is faster!! lolol"; this may sound silly, but it's not always easy to understand when two programs written in two completely different languages are "comparable."

Fourth, what does he give up for coding in Fortran? Would he be more productive in C#? Would his code be more readable in Java? Would he be able to more easily interface with other libraries if he'd written his code in python? While performance in a given language isn't clear, there has been research on programmer productivity in a given language, and there are measurable differences.

Fifth, there's more to performance than running time. How much memory did the program use? Often compilers will make trade-offs between memory use and run time performance. How many lines of code are in the program? There's a clear, documented relationship between SLOC and the presence of defects: more code, more defects.

Lastly, a working program always outperforms a broken program. Too many scientists and engineers fall into the premature optimization trap while omitting to include proper testing of their algorithms. Do you unit test? Do you have some "sanity check" (i.e. run known values through your algorithms, ensure output is correct) to ensure your code actually works? How do you know your program works? Frankly, scientists as a whole completely suck at this; I find many (most???) don't even bother. In my limited and anecdotal experience, they apply scientific rigor to a lot of things except their code.

Anyway, if you’re reasonably sure about .NET being fine for the graphics, I’ll try it again for this project. Once I’m done I’ll share the code for it – and hopefully be able to report that it works well.


Not 100% sure, but pretty sure. C/C++ is probably the no-brainer choice (generally very good compilers, plenty of low-level flexibility), but those languages are a pain in the ass to use (you can spend weeks or months tracking down some of the worst types of bugs). They're a bit more tolerable with something like the Qt framework.

Fortran, I know nothing about, so I can't comment.

So, last question. If I use C# 4.5 on Visual Studio 2012 (may as well design for the future, right?), probably using WPF, what general approach would you recommend for the graphics area? i.e., what class should I use as a drawing board, what functions should I use to draw my objects, and, if you happen to have the time to make such a recommendation, where should I think about putting new thread calls?


Let me get back to you on that one.
User avatar
kidjan
Active Member
 
Posts: 1926
Joined: 25 Jul 2007
Location: Earth.
Blog: View Blog (0)


Re: How to Make a PowerPoint-like GUI?

Postby Natural ChemE on April 19th, 2012, 10:23 pm 

kidjan,

Scientists are bad programmers? Pft. Check out my perfectly formatted Physics homework:

PS6_2.java
Code: Select all
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;


public class PS6_P2 {
   //condition options
   static final int iterationCount = 10000;  //25000
   static final int pointCount = 100;  //50
   static final double boundaryDistanceFromCenter = 3.5;
   static final double squareWellHalfWidth = 3.4;
   static final double squareWellHeight = 250000.0;
   static final double potentialInSquareWell = 0.0;
   static final double initialStateStandardDeviation = 0.5;  //smaller values result in a more needle-like initial distribution
   static final double potentialWellCenter = 2.1;

   //mathematical and physical constants
   static final double mass = 1.0;  //need to pick out
   static final double pi   = Math.PI;
   static final double hbar = 1.0;  //need to figure out units and fix this one //!!!

   //output options
   static final boolean ShowInitialConditionsGraph = false;  //show a graph at the start with the initial distribution?
   static final int graphHeight = 35; //= 45;//= 20;   //needs to be at least (about) 10  (or else the space allotted to the actual graphing area is negative)
   static final int graphWidth = 150;  //= 200;//100;   //needs to be at least (about) 10  (or else the space allotted to the actual graphing area is negative)
   static final int printEveryThisManyGenerations = 10;//5000;
   static final boolean showMatricesOnIteration = false;  //WARNING:  Tons of spam.  Strongly suggested to use low point counts AND low iteration counts when set to 'true'.
   static final boolean recordDataLog = false;  //helps spare the hard drive if set to false when just watching movies
   static final String dataLogFileName = "Data_log.txt";
   static final boolean showSquaredWaveFunctionInGraphs = true;  //show the squared wavefunction in the graphs?
   static final boolean showRandIinIterationGraphs = false;  //show the real and imaginary values in the graphs?
   static final boolean showLegends = true;  //show a legend below the graphs?
   static final boolean useCustomFDomain = true;  //if "true", uses "minFInGraphs" and "maxFInGraphs" for y-axis boundaries.  if false, graph automatically uses data to determine y-axis boundaries (may shift scale each plot)
   static final double minFInGraphs = 0.0;  //"-1.0" is good if plotting real and imaginary components.  "0.0" is good if just plotting squared wavefunction
   static final double maxFInGraphs = 1.0;

   //calculated values
   static final int pointCountMinusOne = pointCount - 1;
   static final double spaceStep       =  2.0 * boundaryDistanceFromCenter / pointCountMinusOne;
   static final double timeStep        = spaceStep * 20000.0;  //should probably calculate this for stability
   static final double alpha           = timeStep / (4.0 * mass * spaceStep * spaceStep);
   static final double gamma           = timeStep / (2.0 * mass * spaceStep * spaceStep);
   static final double eta             = timeStep / (2.0 * hbar);

   //calculation method
   static final boolean useTwoMatrices = true;  //mathematically equivalent, but 'true' flag is faster (two NxN matrices instead of one 2Nx2N matrix)


   @SuppressWarnings("unused")
   public static void main(String argv[]){
      try {
         BufferedWriter writer = new BufferedWriter(new FileWriter(dataLogFileName));

         if(recordDataLog){writer.write("time");}

         double[] x       = new double[pointCount];
         double[] R       = new double[pointCount];
         double[] I       = new double[pointCount];
         double[] V       = new double[pointCount];
         double[] modV    = new double[pointCount];
         double[] scriptR = new double[pointCount];
         double[] scriptI = new double[pointCount];
         double[] psiSq   = new double[pointCount];
         int matrixFarRightIndex;
         int printIndex = 0, twoI;
         Matrix matrix, matrix2;
         if (useTwoMatrices){
            matrixFarRightIndex = pointCount;
            matrix = new Matrix(matrixFarRightIndex, matrixFarRightIndex + 1);
            matrix2 = new Matrix(matrixFarRightIndex, matrixFarRightIndex + 1);
         } else {
            matrixFarRightIndex = 2 * pointCount;
            matrix = new Matrix(matrixFarRightIndex, matrixFarRightIndex + 1);
         }
         Graph graph;

         //setup initial values
         for (int i=0; i<pointCount; i++){
            x[i] = -boundaryDistanceFromCenter + spaceStep * (double)i;
            if(Math.abs(x[i])<squareWellHalfWidth){R[i] = Math.exp(-(x[i]-potentialWellCenter)*(x[i]-potentialWellCenter) / (2.0 * initialStateStandardDeviation * initialStateStandardDeviation)) / (Math.sqrt(2.0 * Math.PI)*initialStateStandardDeviation);}
            else {R[i] = 0.0;}
            I[i] = 0.0;  //?  Not sure what to do for this one, but this works.
            V[i] = (Math.abs(x[i])>squareWellHalfWidth)?squareWellHeight:potentialInSquareWell;
            modV[i] = gamma + eta * V[i];
            psiSq[i] = R[i] * R[i] + I[i] * I[i];
         }
         Normalize(x, R, I);
         
         //graph initial values if it's called for
         if(ShowInitialConditionsGraph){
            graph = new Graph(graphHeight, graphWidth);
            if(useCustomFDomain){graph.UseCustomFWindow(minFInGraphs, maxFInGraphs);}
            //graph.Add(x, V, 'V', "Potential");
            graph.Add(x, R, 'R', "Real component of wavefunction");
            graph.Add(x, I, 'I', "Imaginary component of wavefunction");
            graph.Add(x, psiSq, 'X', "Squared wavefunction");
            graph.Print("Initial conditions");
            if(showLegends){graph.PrintLegend();}
         }

         //write the header to the data log if it's called for
         if (recordDataLog){
            for (int i=0; i<pointCount; i++){
               writer.write("\t" + x[i]);
            }
            writer.newLine();
            writer.write(Double.toString(0.0));
            for (int i=0; i<pointCount; i++){
               writer.write("\t" + psiSq[i]);
            }
         }

         //start iterating through time for the wave function
         for (int iterationI=0; iterationI<iterationCount; iterationI++){
            //clear matrix
            if(useTwoMatrices){
               for (int i=0; i<pointCount; i++){
                  for (int j=0; j<pointCount; j++){
                     matrix.M[i][j] = 0.0;
                     matrix2.M[i][j] = 0.0;
                  }
               }
            } else {
               for (int i=0; i<2*pointCount; i++){
                  for (int j=0; j<2*pointCount; j++){
                     matrix.M[i][j] = 0.0;
                  }
               }
            }

            //prepare data from prior generation
            scriptR[0] = R[0] + modV[0] * I[0] - alpha * I[1];
            scriptI[0] = I[0] - modV[0] * R[0] + alpha * R[1];
            for (int i=1; i<pointCountMinusOne; i++){
               scriptR[i] = R[i] - alpha * I[i-1] + modV[i] * I[i] - alpha * I[i+1];
               scriptI[i] = I[i] + alpha * R[i-1] - modV[i] * R[i] + alpha * R[i+1];
            }
            scriptR[pointCountMinusOne] = R[pointCountMinusOne] - alpha * I[pointCountMinusOne-1] + modV[pointCountMinusOne] * I[pointCountMinusOne];
            scriptI[pointCountMinusOne] = I[pointCountMinusOne] + alpha * R[pointCountMinusOne-1] - modV[pointCountMinusOne] * R[pointCountMinusOne];

            //setup new matrix
            //  top row
            //    set far-right column
            if (useTwoMatrices){
               matrix.M[0][matrixFarRightIndex] = -scriptR[0];
               matrix2.M[0][matrixFarRightIndex] = -scriptI[0];
               //    set R
               matrix.M[0][0] = modV[0];
               matrix.M[0][1] = -alpha;
               //    set I
               matrix2.M[0][0] = -modV[0];
               matrix2.M[0][1] = alpha;
            } else {
               matrix.M[0][matrixFarRightIndex] = -scriptR[0];
               matrix.M[1][matrixFarRightIndex] = -scriptI[0];
               //    set R
               matrix.M[0][1] = modV[0];
               matrix.M[0][3] = -alpha;
               //    set I
               matrix.M[1][0] = -modV[0];
               matrix.M[1][2] = alpha;
            }

            //    set in intermediate rows (all rows except for the first and the last)
            for (int i=1; i<pointCountMinusOne; i++){
               if (useTwoMatrices){
                  //set far-right column
                  matrix.M[i][matrixFarRightIndex]  = -scriptR[i];
                  matrix2.M[i][matrixFarRightIndex] = -scriptI[i];

                  //set R
                  matrix.M[i][i-1] = -alpha;
                  matrix.M[i][i  ] = modV[i];
                  matrix.M[i][i+1] = -alpha;

                  //set I
                  matrix2.M[i][i-1] = alpha;
                  matrix2.M[i][i  ] = -modV[i];
                  matrix2.M[i][i+1] = alpha;
               } else {
                  twoI = 2*i;

                  //set far-right column
                  matrix.M[twoI][matrixFarRightIndex]   = -scriptR[i];
                  matrix.M[twoI+1][matrixFarRightIndex] = -scriptI[i];

                  //set R
                  matrix.M[twoI][twoI-1] = -alpha;
                  matrix.M[twoI][twoI+1] = modV[i];
                  matrix.M[twoI][twoI+3] = -alpha;

                  //set I
                  matrix.M[twoI+1][twoI-2] = alpha;
                  matrix.M[twoI+1][twoI] = -modV[i];
                  matrix.M[twoI+1][twoI+2] = alpha;
               }
            }
            if (useTwoMatrices){
               //  bottom row
               //    set far-right column
               matrix.M[pointCountMinusOne][matrixFarRightIndex]  = -scriptR[pointCountMinusOne];
               matrix2.M[pointCountMinusOne][matrixFarRightIndex] = -scriptI[pointCountMinusOne];
               //    set R
               matrix.M[pointCountMinusOne][pointCountMinusOne-1] = -alpha;
               matrix.M[pointCountMinusOne][pointCountMinusOne] = modV[pointCountMinusOne];
               //    set I
               matrix2.M[pointCountMinusOne][pointCountMinusOne-1] = alpha;   //these lines correct?
               matrix2.M[pointCountMinusOne][pointCountMinusOne] = -modV[pointCountMinusOne];
            } else {
               //  bottom row
               //    set far-right column
               matrix.M[2*pointCountMinusOne][matrixFarRightIndex]   = -scriptR[pointCountMinusOne];
               matrix.M[2*pointCountMinusOne+1][matrixFarRightIndex] = -scriptI[pointCountMinusOne];
               //    set R
               matrix.M[2*pointCountMinusOne][2*pointCountMinusOne-1] = -alpha;
               matrix.M[2*pointCountMinusOne][2*pointCountMinusOne+1] = modV[pointCountMinusOne];
               //    set I
               matrix.M[2*pointCountMinusOne+1][2*pointCountMinusOne-2] = alpha;
               matrix.M[2*pointCountMinusOne+1][2*pointCountMinusOne] = -modV[pointCountMinusOne];
            }

            if (useTwoMatrices){
               if(showMatricesOnIteration){matrix.Print("Matrix #1, pre-elimination"); matrix2.Print("Matrix #2, pre-elimination");}
               //eliminate
               matrix.SimpleGaussJordanElimination();
               matrix2.SimpleGaussJordanElimination();
               if(showMatricesOnIteration){matrix.Print("Matrix #1, post-elimination"); matrix2.Print("Matrix #2, post-elimination");}
            } else {
               if(showMatricesOnIteration){matrix.Print("Pre-elimination");}
               //eliminate
               matrix.SimpleGaussJordanElimination();
               if(showMatricesOnIteration){matrix.Print("Post-elimination");}
            }

            if (useTwoMatrices){
               //recover R and I
               for (int i=0; i<pointCount; i++){
                  R[i] = matrix2.M[i][matrixFarRightIndex];
                  I[i] = matrix.M[i][matrixFarRightIndex];
               }
            } else {
               //recover R and I
               for (int i=0; i<pointCount; i++){
                  R[i] = matrix.M[2*i  ][matrixFarRightIndex];
                  I[i] = matrix.M[2*i+1][matrixFarRightIndex];
               }
            }

            //calculate squared wave function
            for (int i=0; i<pointCount; i++){
               psiSq[i] = R[i] * R[i] + I[i] * I[i];
            }


            if (recordDataLog){
               writer.newLine();
               writer.write(Double.toString(timeStep * (1.0+(double)iterationI)));
               for (int i=0; i<pointCount; i++){
                  writer.write("\t" + psiSq[i]);
               }
            }
            if(++printIndex>=printEveryThisManyGenerations){
               printIndex = 0;
               graph = new Graph(graphHeight, graphWidth);
               if(useCustomFDomain){graph.UseCustomFWindow(minFInGraphs, maxFInGraphs);}
               //graph.Add(x, V, 'V', "Potential");
               if(showRandIinIterationGraphs){
                  graph.Add(x, R, 'R', "Real component of wavefunction");
                  graph.Add(x, I, 'I', "Imaginary component of wavefunction");
               }
               if(showSquaredWaveFunctionInGraphs){graph.Add(x, psiSq, 'X', "Squared wavefunction");}
               graph.Print("Squared wavefunction at iteration #" + iterationI);
               if(showLegends){graph.PrintLegend();}
            }
         }
      } catch (IOException e) {
         e.printStackTrace();
         System.out.println("Error:  Could not create file " + dataLogFileName + ".");
      }
   }
   private static void Normalize(double[] toNormalize){
      int length = toNormalize.length;
      double sum = 0.0;
      for (int i=0; i<length; i++){sum += toNormalize[i];}
      sum *= spaceStep;
      for (int i=0; i<length; i++){toNormalize[i] /= sum;}
   }
   private static void Normalize(double[] x, double[] R, double[] I){
      double sum = 0.0;
      int loopCount = (R.length<I.length)?R.length:I.length;
      for (int i=0; i<loopCount; i++){sum += R[i]*R[i] + I[i]*I[i];}
      sum = Math.sqrt(sum * (x[loopCount-1]-x[0]) / (double)(loopCount-1));
      for (int i=0; i<loopCount; i++){
         R[i] /= sum;
         I[i] /= sum;
      }
   }
}


PrintSpace.java
Code: Select all
//The purpose of this class will be to provide a basic ASCII-art
//basis.

public class PrintSpace {
   final static char voidCharacter = ' ';
   final static char aloneCharacter = 'x';
   final static char downArrow = 'v';
   final static char upArrow = '^';
   final static char leftArrow = '<';
   final static char rightArrow = '>';
   final static char rightDownDiagonal = '\\';
   final static char rightUpDiagonal = '/';
   final static char horizontalConnection = '-';
   final static char verticalConnection = '|';
   final static char hortizontalAndVerticalConnection = '+';
   final static char bothDiagonalsConnection = 'X';
   final static char majorConnector = 'X';
   final static char unresolvedCharacter = '?';

   char[][] space;
   int[][] layer;
   boolean[][] connection;
   boolean resolutionIsFloating;
   int height, width;
   public PrintSpace(int height, int width){
      this.height = height;
      this.width = width;
      space = new char[height][width];
      layer = new int [height][width];
      for (int i=0; i<height; i++){
         for (int j=0; j<width; j++){
            space[i][j] = voidCharacter;
            layer[i][j] = 0;
         }
      }
      resolutionIsFloating = false;
   }
   public void AddTextLeftAlign(String toAdd, int x, int y, int layer){
      int printTo = (y+toAdd.length()<=width)?toAdd.length():width-y;
      for (int i=0; i<printTo; i++){Add(toAdd.charAt(i), x, y+i, layer);}
   }
   public void AddTextCenterAlign(String toAdd, int x, int y, int layer){
      int offset = toAdd.length() / 2;
      int printTo = (y+toAdd.length()-offset<=width)?toAdd.length():width-y+offset;
      for (int i=0; i<printTo; i++){Add(toAdd.charAt(i), x, y+i-offset, layer);}
   }
   //works but silly
   /*public void AddTextBackwards(String toAdd, int x, int y, int layer){
      int printTo = (y-toAdd.length()>=-1)?toAdd.length():y+1;
      for (int i=0; i<printTo; i++){Add(toAdd.charAt(i), x, y-i, layer);}
   }*/
   public void Add(char toAdd, int x, int y, int layer){
      if (layer >= this.layer[x][y]){this.space[x][y] = toAdd;}
   }
   public void DrawLine(int x1, int y1, int x2, int y2, int layer){
      FloatLine(x1, y1, x2, y2);
      Resolve(layer);
   }
   public void FloatLine (int x1, int y1, int x2, int y2){
      int xDiff = x2-x1;
      int yDiff = y2-y1;
      int pointCount = ((xDiff>yDiff)?xDiff:yDiff)+1;
      int[][] points = new int[pointCount][2];
      //System.out.println(pointCount);
      points[0][0] = x1;
      points[0][1] = y1;
      points[pointCount-1][0] = x2;
      points[pointCount-1][1] = y2;
      //System.out.println("Drawing from (" + x1 + "," + y1 + ") to (" + x2 + "," + y2 + ").");
      //System.out.println("Drawing at (" + points[0][0] + "," + points[0][1] + ").");  //!!!  (to remove)
      for (int i=1; i<pointCount-1; i++){
         points[i][0] = x1 + (int)(Math.round(((double)xDiff) * (((double)(i))/((double)(pointCount-1)))));
         points[i][1] = y1 + (int)(Math.round(((double)yDiff) * (((double)(i))/((double)(pointCount-1)))));
         //System.out.println("Drawing at (" + points[i][0] + "," + points[i][1] + ").");  //!!!  (to remove)
      }
      //System.out.println("Drawing at (" + points[points.length-1][0] + "," + points[points.length-1][1] + ").");  //!!!  (to remove)
      SetupResolve();
      for (int i=0; i<pointCount; i++){
         connection[points[i][0]][points[i][1]] = true;
      }
   }
   private void SetupResolve(){
      if (resolutionIsFloating) {return;}
      connection = new boolean[height][width];
      for (int i=0; i<height; i++){
         for (int j=0; j<width; j++){
            connection[i][j] = false;
         }
      }
      resolutionIsFloating = true;
   }
   private char ResolvedCharacter (boolean[] positions){
      //positions[] structure:  //!!start
      //  [0]  [1]  [2]
      //  [3]   X   [4]
      //  [5]  [6]  [7]
      //where "X" is the character to be determined

      //this huge if{}else{} structure may look daunting, but it's basically just one case for
      //each possible set of values for positions[], or 2^8=256 little branches.
      //Long, sure, but very efficient for the computer.
      if(positions[0]){
         if(positions[1]){
            if(positions[2]){
               if (positions[3]){
                  if (positions[4]){
                     if (positions[5]) {
                        if (positions[6]){
                           if (positions[7]){
                              return majorConnector;
                           } else {

                           }
                        } else {
                           if (positions[7]){

                           } else {

                           }
                        }
                     } else {
                        if (positions[6]){
                           if (positions[7]){

                           } else {

                           }
                        } else {
                           if (positions[7]){

                           } else {

                           }
                        }
                     }
                  } else {
                     if (positions[5]) {
                        if (positions[6]){
                           if (positions[7]){

                           } else {

                           }
                        } else {
                           if (positions[7]){

                           } else {

                           }
                        }
                     } else {
                        if (positions[6]){
                           if (positions[7]){

                           } else {

                           }
                        } else {
                           if (positions[7]){

                           } else {

                           }
                        }
                     }
                  }
               } else {
                  if (positions[4]){
                     if (positions[5]) {
                        if (positions[6]){
                           if (positions[7]){

                           } else {

                           }
                        } else {
                           if (positions[7]){

                           } else {

                           }
                        }
                     } else {
                        if (positions[6]){
                           if (positions[7]){

                           } else {

                           }
                        } else {
                           if (positions[7]){

                           } else {

                           }
                        }
                     }
                  } else {
                     if (positions[5]) {
                        if (positions[6]){
                           if (positions[7]){

                           } else {

                           }
                        } else {
                           if (positions[7]){

                           } else {

                           }
                        }
                     } else {
                        if (positions[6]){
                           if (positions[7]){

                           } else {
                              return downArrow;
                              //return verticalConnection; //conflict again, think that the above is correct
                           }
                        } else {
                           if (positions[7]){

                           } else {
                              return downArrow;
                           }
                        }
                     }
                  }
               }
            } else {
               if (positions[3]){
                  if (positions[4]){
                     if (positions[5]) {
                        if (positions[6]){
                           if (positions[7]){

                           } else {

                           }
                        } else {
                           if (positions[7]){

                           } else {

                           }
                        }
                     } else {
                        if (positions[6]){
                           if (positions[7]){

                           } else {

                           }
                        } else {
                           if (positions[7]){

                           } else {

                           }
                        }
                     }
                  } else {
                     if (positions[5]) {
                        if (positions[6]){
                           if (positions[7]){

                           } else {

                           }
                        } else {
                           if (positions[7]){

                           } else {

                           }
                        }
                     } else {
                        if (positions[6]){
                           if (positions[7]){

                           } else {

                           }
                        } else {
                           if (positions[7]){

                           } else {

                           }
                        }
                     }
                  }
               } else {
                  if (positions[4]){
                     if (positions[5]) {
                        if (positions[6]){
                           if (positions[7]){

                           } else {

                           }
                        } else {
                           if (positions[7]){

                           } else {

                           }
                        }
                     } else {
                        if (positions[6]){
                           if (positions[7]){

                           } else {

                           }
                        } else {
                           if (positions[7]){

                           } else {

                           }
                        }
                     }
                  } else {
                     if (positions[5]) {
                        if (positions[6]){
                           if (positions[7]){

                           } else {

                           }
                        } else {
                           if (positions[7]){

                           } else {

                           }
                        }
                     } else {
                        if (positions[6]){
                           if (positions[7]){

                           } else {
                              return verticalConnection;
                           }
                        } else {
                           if (positions[7]){

                           } else {

                           }
                        }
                     }
                  }
               }
            }
         } else {  //2TF
            if(positions[2]){
               if (positions[3]){
                  if (positions[4]){
                     if (positions[5]) {
                        if (positions[6]){
                           if (positions[7]){

                           } else {

                           }
                        } else {
                           if (positions[7]){

                           } else {

                           }
                        }
                     } else {
                        if (positions[6]){
                           if (positions[7]){

                           } else {

                           }
                        } else {
                           if (positions[7]){

                           } else {
                              return horizontalConnection;
                           }
                        }
                     }
                  } else {
                     if (positions[5]) {
                        if (positions[6]){
                           if (positions[7]){

                           } else {

                           }
                        } else {
                           if (positions[7]){

                           } else {

                           }
                        }
                     } else {
                        if (positions[6]){
                           if (positions[7]){

                           } else {

                           }
                        } else {
                           if (positions[7]){

                           } else {

                           }
                        }
                     }
                  }
               } else {
                  if (positions[4]){
                     if (positions[5]) {
                        if (positions[6]){
                           if (positions[7]){

                           } else {

                           }
                        } else {
                           if (positions[7]){

                           } else {
                              return rightArrow;
                           }
                        }
                     } else {
                        if (positions[6]){
                           if (positions[7]){

                           } else {

                           }
                        } else {
                           if (positions[7]){

                           } else {

                           }
                        }
                     }
                  } else {
                     if (positions[5]) {
                        if (positions[6]){
                           if (positions[7]){

                           } else {
                              return downArrow;
                           }
                        } else {
                           if (positions[7]){
                              return bothDiagonalsConnection;
                           } else {
                              return bothDiagonalsConnection;
                              //return rightArrow;
                           }
                        }
                     } else {
                        if (positions[6]){
                           if (positions[7]){

                           } else {

                           }
                        } else {
                           if (positions[7]){

                           } else {
                              return bothDiagonalsConnection;
                              //return downArrow;
                           }
                        }
                     }
                  }
               }
            } else {
               if (positions[3]){
                  if (positions[4]){
                     if (positions[5]) {
                        if (positions[6]){
                           if (positions[7]){

                           } else {

                           }
                        } else {
                           if (positions[7]){

                           } else {
                              return rightArrow;
                           }
                        }
                     } else {
                        if (positions[6]){
                           if (positions[7]){

                           } else {

                           }
                        } else {
                           if (positions[7]){

                           } else {
                              return horizontalConnection;
                           }
                        }
                     }
                  } else {
                     if (positions[5]) {
                        if (positions[6]){
                           if (positions[7]){

                           } else {

                           }
                        } else {
                           if (positions[7]){

                           } else {
                              return rightArrow;
                           }
                        }
                     } else {
                        if (positions[6]){
                           if (positions[7]){

                           } else {

                           }
                        } else {
                           if (positions[7]){

                           } else {

                           }
                        }
                     }
                  }
               } else {
                  if (positions[4]){
                     if (positions[5]) {
                        if (positions[6]){
                           if (positions[7]){

                           } else {

                           }
                        } else {
                           if (positions[7]){

                           } else {

                           }
                        }
                     } else {
                        if (positions[6]){
                           if (positions[7]){

                           } else {

                           }
                        } else {
                           if (positions[7]){

                           } else {

                           }
                        }
                     }
                  } else {
                     if (positions[5]) {
                        if (positions[6]){
                           if (positions[7]){

                           } else {
                              
                           }
                        } else {
                           if (positions[7]){
                              return bothDiagonalsConnection;
                           } else {

                           }
                        }
                     } else {
                        if (positions[6]){
                           if (positions[7]){

                           } else {

                           }
                        } else {
                           if (positions[7]){
                              return rightDownDiagonal;
                           } else {
                              return rightDownDiagonal;
                           }
                        }
                     }
                  }
               }
            }
         }
      } else {  //1F
         if(positions[1]){
            if(positions[2]){
               if (positions[3]){
                  if (positions[4]){
                     if (positions[5]) {
                        if (positions[6]){
                           if (positions[7]){

                           } else {

                           }
                        } else {
                           if (positions[7]){

                           } else {

                           }
                        }
                     } else {
                        if (positions[6]){
                           if (positions[7]){

                           } else {

                           }
                        } else {
                           if (positions[7]){

                           } else {

                           }
                        }
                     }
                  } else {
                     if (positions[5]) {
                        if (positions[6]){
                           if (positions[7]){

                           } else {

                           }
                        } else {
                           if (positions[7]){

                           } else {

                           }
                        }
                     } else {
                        if (positions[6]){
                           if (positions[7]){

                           } else {

                           }
                        } else {
                           if (positions[7]){

                           } else {

                           }
                        }
                     }
                  }
               } else {
                  if (positions[4]){
                     if (positions[5]) {
                        if (positions[6]){
                           if (positions[7]){

                           } else {

                           }
                        } else {
                           if (positions[7]){

                           } else {

                           }
                        }
                     } else {
                        if (positions[6]){
                           if (positions[7]){

                           } else {

                           }
                        } else {
                           if (positions[7]){

                           } else {

                           }
                        }
                     }
                  } else {
                     if (positions[5]) {
                        if (positions[6]){
                           if (positions[7]){

                           } else {

                           }
                        } else {
                           if (positions[7]){

                           } else {

                           }
                        }
                     } else {
                        if (positions[6]){
                           if (positions[7]){

                           } else {
                              return verticalConnection;
                           }
                        } else {
                           if (positions[7]){

                           } else {

                           }
                        }
                     }
                  }
               }
            } else {  //3FTF
               if (positions[3]){
                  if (positions[4]){
                     if (positions[5]) {
                        if (positions[6]){
                           if (positions[7]){

                           } else {

                           }
                        } else {
                           if (positions[7]){

                           } else {

                           }
                        }
                     } else {
                        if (positions[6]){
                           if (positions[7]){

                           } else {
                              return hortizontalAndVerticalConnection;
                           }
                        } else {
                           if (positions[7]){

                           } else {
                              return hortizontalAndVerticalConnection;
                           }
                        }
                     }
                  } else {
                     if (positions[5]) {
                        if (positions[6]){
                           if (positions[7]){

                           } else {

                           }
                        } else {
                           if (positions[7]){

                           } else {

                           }
                        }
                     } else {
                        if (positions[6]){
                           if (positions[7]){

                           } else {
                              return hortizontalAndVerticalConnection;
                           }
                        } else {
                           if (positions[7]){

                           } else {
                              return hortizontalAndVerticalConnection;
                           }
                        }
                     }
                  }
               } else {
                  if (positions[4]){
                     if (positions[5]) {
                        if (positions[6]){
                           if (positions[7]){

                           } else {

                           }
                        } else {
                           if (positions[7]){

                           } else {

                           }
                        }
                     } else {
                        if (positions[6]){
                           if (positions[7]){

                           } else {

                           }
                        } else {
                           if (positions[7]){

                           } else {
                              return hortizontalAndVerticalConnection;
                           }
                        }
                     }
                  } else {
                     if (positions[5]) {
                        if (positions[6]){
                           if (positions[7]){
                              return upArrow;
                              //return verticalConnection; //conflict, think this one is wrong
                              //this should be 1, 5, 6, 7
                           } else {
                              return verticalConnection;
                           }
                        } else {
                           if (positions[7]){
                              return upArrow;
                           } else {

                           }
                        }
                     } else {
                        if (positions[6]){
                           if (positions[7]){
                              return verticalConnection;
                           } else {
                              return verticalConnection;
                           }
                        } else {
                           if (positions[7]){

                           } else {
                              return verticalConnection;
                           }
                        }
                     }
                  }
               }
            }
         } else { //2FF
            if(positions[2]){
               if (positions[3]){
                  if (positions[4]){
                     if (positions[5]) {
                        if (positions[6]){
                           if (positions[7]){

                           } else {

                           }
                        } else {
                           if (positions[7]){

                           } else {

                           }
                        }
                     } else {
                        if (positions[6]){
                           if (positions[7]){

                           } else {

                           }
                        } else {
                           if (positions[7]){
                              return leftArrow;
                           } else {
                              return horizontalConnection;
                           }
                        }
                     }
                  } else {
                     if (positions[5]) {
                        if (positions[6]){
                           if (positions[7]){

                           } else {

                           }
                        } else {
                           if (positions[7]){

                           } else {

                           }
                        }
                     } else {
                        if (positions[6]){
                           if (positions[7]){

                           } else {

                           }
                        } else {
                           if (positions[7]){
                              return leftArrow;
                           } else {

                           }
                        }
                     }
                  }
               } else {
                  if (positions[4]){
                     if (positions[5]) {
                        if (positions[6]){
                           if (positions[7]){

                           } else {

                           }
                        } else {
                           if (positions[7]){

                           } else {

                           }
                        }
                     } else {
                        if (positions[6]){
                           if (positions[7]){

                           } else {

                           }
                        } else {
                           if (positions[7]){
                              return leftArrow;
                           } else {

                           }
                        }
                     }
                  } else {
                     if (positions[5]) {
                        if (positions[6]){
                           if (positions[7]){

                           } else {

                           }
                        } else {
                           if (positions[7]){

                           } else {
                              return rightUpDiagonal;
                           }
                        }
                     } else {
                        if (positions[6]){
                           if (positions[7]){

                           } else {

                           }
                        } else {
                           if (positions[7]){
                              return leftArrow;
                           } else {
                              return rightUpDiagonal;
                           }
                        }
                     }
                  }
               }
            } else { //3FFF
               if (positions[3]){
                  if (positions[4]){
                     if (positions[5]) {
                        if (positions[6]){
                           if (positions[7]){
                              
                           } else {

                           }
                        } else {
                           if (positions[7]){
                              return horizontalConnection;
                           } else {
                              return horizontalConnection;
                           }
                        }
                     } else {
                        if (positions[6]){
                           if (positions[7]){

                           } else {
                              return hortizontalAndVerticalConnection;
                           }
                        } else {
                           if (positions[7]){
                              return horizontalConnection;
                           } else {
                              return horizontalConnection;
                           }
                        }
                     }
                  } else {
                     if (positions[5]) {
                        if (positions[6]){
                           if (positions[7]){

                           } else {

                           }
                        } else {
                           if (positions[7]){

                           } else {

                           }
                        }
                     } else {
                        if (positions[6]){
                           if (positions[7]){

                           } else {
                              return hortizontalAndVerticalConnection;
                           }
                        } else {
                           if (positions[7]){

                           } else {
                              return horizontalConnection;
                           }
                        }
                     }
                  }
               } else { //4FFFF
                  if (positions[4]){
                     if (positions[5]) {
                        if (positions[6]){
                           if (positions[7]){

                           } else {

                           }
                        } else {
                           if (positions[7]){

                           } else {

                           }
                        }
                     } else {
                        if (positions[6]){
                           if (positions[7]){

                           } else {
                              return hortizontalAndVerticalConnection;
                           }
                        } else {
                           if (positions[7]){

                           } else {
                              return horizontalConnection;
                           }
                        }
                     }
                  } else {
                     if (positions[5]) {
                        if (positions[6]){
                           if (positions[7]){
                              return upArrow;
                           } else {

                           }
                        } else {
                           if (positions[7]){
                              return upArrow;
                           } else {
                              return rightUpDiagonal;
                           }
                        }
                     } else {
                        if (positions[6]){
                           if (positions[7]){

                           } else {
                              return verticalConnection;
                           }
                        } else {
                           if (positions[7]){
                              return rightDownDiagonal;
                           } else {
                              return aloneCharacter;
                           }
                        }
                     }
                  }
               }
            }
         }
      }

      return majorConnector;
      //return unresolvedCharacter;
   }
   private boolean IsInSpace(int x, int y){
      //"(" + x + "," + y + ") is NOT in space (" + width + "," + height + ")");
      if (x<0 || x>=height){return false;}
      if (y<0 || y>=width ){return false;}
      return true;
   }
   public void Resolve(int layer){
      if(!resolutionIsFloating){return;}

      boolean[] charR = new boolean[8];
      //charR[] structure:
      //  [0]  [1]  [2]
      //  [3]   X   [4]
      //  [5]  [6]  [7]

      for (int i=0; i<height; i++){
         for (int j=0; j<width; j++){
            if (connection[i][j]){
               charR[0] = IsInSpace(i-1,j-1)?connection[i-1][j-1]:false;
               charR[1] = IsInSpace(i-1,j  )?connection[i-1][j  ]:false;
               charR[2] = IsInSpace(i-1,j+1)?connection[i-1][j+1]:false;
               charR[3] = IsInSpace(i  ,j-1)?connection[i  ][j-1]:false;
               charR[4] = IsInSpace(i  ,j+1)?connection[i  ][j+1]:false;
               charR[5] = IsInSpace(i+1,j-1)?connection[i+1][j-1]:false;
               charR[6] = IsInSpace(i+1,j  )?connection[i+1][j  ]:false;
               charR[7] = IsInSpace(i+1,j+1)?connection[i+1][j+1]:false;
               Add(ResolvedCharacter(charR), i, j, layer);
            }
         }
      }

      resolutionIsFloating = false;
   }
   public void FloatAll(){
      SetupResolve();
      for (int i=0; i<height; i++){
         for (int j=0; j<width; j++){
            if (space[i][j] != voidCharacter) {
               connection[i][j] = true;
            }
         }
      }
   }
   public void PushAllToLayer(int layer){
      for (int i=0; i<height; i++){
         for (int j=0; j<width; j++){
            this.layer[i][j] = layer;
         }
      }
   }
   public void ReResolve(){
      FloatAll();   //any int works
      Resolve (0);   //any int works
   }
   public void Print(){
      for (int i=0; i<height; i++){
         for (int j=0; j<width; j++){
            System.out.print(space[i][j]);
         }
         System.out.println();
      }
   }
}



Matrix.java
Code: Select all
import java.math.BigDecimal;
import java.math.MathContext;


public class Matrix {
   protected double M[][];
   protected int rowCount, colCount;
   protected boolean unCol; //unconstrained/undefined columns, i.e., columns with all zero values

   //for taking out suspected rounding errors
   final private static double postNormalizationRoundingErrorThreshold = 1e-14;
   
   public Matrix(double[][] matrix){
      rowCount = matrix.length;
      colCount = matrix[0].length;
      M = new double[rowCount][colCount];
      for (int i=0; i<rowCount; i++){
         for (int j=0; j<colCount; j++){
            M[i][j] = matrix[i][j];
         }
      }
   }
   public Matrix (double[] columnMatrix){
      rowCount = columnMatrix.length;
      colCount = 1;
      M = new double[rowCount][colCount];
      for (int i=0; i<rowCount; i++){
         M[i][0] = columnMatrix[i];
      }
   }
   public Matrix (int[] columnMatrix){
      rowCount = columnMatrix.length;
      colCount = 1;
      M = new double[rowCount][colCount];
      for (int i=0; i<rowCount; i++){
         M[i][0] = (double)columnMatrix[i];
      }
   }
   public Matrix(int height, int width){
      M = new double[height][width];
      ResetLengths();
   }
   public static Matrix Clone(Matrix matrixToClone){
      return new Matrix(matrixToClone.M);
   }
   public Matrix Clone(){
      return new Matrix(M);
   }
   public static void Print (double[][] matrix, String title){
      int height = matrix.length, width = matrix[0].length;
      if (title != "") {System.out.println(title.trim() + ":");}
      for (int i=0; i<height; i++){
         System.out.print("[ ");
         for (int j=0; j<width; j++){
            System.out.print(DD(matrix[i][j]) + " ");
         }
         System.out.println(" ]");
      }
   }
   public void Print (String title){
      if (title != "") {System.out.println(title.trim() + ":");}
      for (int i=0; i<rowCount; i++){
         System.out.print("[ ");
         for (int j=0; j<colCount; j++){
            System.out.print(DD(M[i][j]) + " ");
         }
         System.out.println(" ]");
      }
   }
   private static String DD (double toDisplay){
      String toReturn;
      if (Double.isNaN(toDisplay)) {
         toReturn = "NaN";
      } else if (Double.isInfinite(toDisplay)){
         toReturn = "Inf";
      } else {
         toReturn = BigDecimal.valueOf(toDisplay).round(new MathContext(3)).toString();
      }
      while (toReturn.length() < 10) {
         toReturn += " ";
         if (toReturn.length() < 10) {toReturn = " " + toReturn;}
      }
      return toReturn;
   }
   public static double[][] Normalize (double[][] matrixToNormalize){
      double max, toReturn[][] = matrixToNormalize.clone();
      int rowCount = toReturn.length, colCount = toReturn[0].length;
      //rescale
      //rescale diagonal elements to 1
      for (int i=0; i<((rowCount<colCount)?rowCount:colCount); i++){for (int j=0; j<colCount; j++) {
         toReturn[i][j] /= toReturn[i][i];
      }}
      //rescale elements beyond the diagonal by bringing max in row to 1
      for (int i=colCount; i<rowCount; i++){
         max = toReturn[i][0];
         for (int j=1; j<colCount; j++) {if (toReturn[i][j] > max) {max = toReturn[i][j];}}
         for (int j=0; j<colCount; j++) {toReturn[i][j] /= max;}
      }
      return toReturn;
   }
   public void Normalize(int numberOfColumnsOnTheRightToExclude){
      //rescale based on the largest magnitude in a row that's not in an excluded column
      int allowedColCount = colCount - numberOfColumnsOnTheRightToExclude;
      int maxIndex;
      double maxValue, thisValue;
      for (int i=0; i<rowCount; i++){
         maxIndex = -1;
         maxValue = 0.0;
         for (int j=0; j<allowedColCount; j++){
            thisValue = Math.abs(this.M[i][j]);
            if (thisValue > maxValue){
               maxValue = thisValue;
               maxIndex = j;
            }
         }
         if (maxIndex < 0 || IsZero(maxValue)) {continue;}  //don't normalize an all-zero row
         maxValue = M[i][maxIndex];  //to get rid of the absolute value modification to the max value
         for (int j=0; j<colCount; j++){
            M[i][j] /= maxValue;
         }
      }
   }
   /*public void Normalize (){
      double max, divisor;
      double[] temp = new double[colCount];
      //rescale
      //rescale diagonal elements to 1
      //System.out.println(colCount);
      for (int i=0; i<((rowCount<colCount)?rowCount:colCount); i++){
         divisor = M[i][i];
         for (int j=0; j<colCount; j++) {
            M[i][j] /= divisor;
         }
      }

      //rescale elements beyond the diagonal by bringing max in row to 1
      for (int i=colCount; i<rowCount; i++){
         max = M[i][0];
         for (int j=1; j<colCount; j++) {if (M[i][j] > max) {max = M[i][j];}}
         for (int j=0; j<colCount; j++) {M[i][j] /= max;}
      }
   }*/
   public static double[][] GaussJordanElimination (double[][] matrix){
      //needs to be updated to be on-par with the non-static version
      int rowCount = matrix.length, colCount = matrix[0].length;
      double toReturn[][] = matrix.clone(), ratio, max;
      //System.out.println(matrix[0][1]);

      //eliminate
      for (int k=0; k<rowCount; k++){
         for (int i=0; i<rowCount; i++){if (i!=k && i<colCount && k<colCount){
            ratio = toReturn[i][k] / toReturn[k][k];
            for (int j=0  ; j<colCount; j++) {toReturn[i][j] -= ratio*toReturn[k][j];}
         }}
      }

      //rescale diagonal elements to 1
      for (int i=0; i<((rowCount<colCount)?rowCount:colCount); i++){for (int j=0; j<colCount; j++) {
         toReturn[i][j] /= toReturn[i][i];
      }}
      //rescale elements beyond the diagonal by bringing max in row to 1
      for (int i=colCount; i<rowCount; i++){
         max = matrix[i][0];
         for (int j=1; j<colCount; j++) {if (matrix[i][j] > max) {max = matrix[i][j];}}
         for (int j=0; j<colCount; j++) {matrix[i][j] /= max;}
      }
      return toReturn;
   }
   private void EliminateZeroRows(){
      boolean[] allZeros = new boolean[rowCount];
      int nonZeroRowCount = rowCount;
      for (int i=0; i<rowCount; i++){
         allZeros[i] = true;
         for (int j=0; j<colCount; j++){
            if(!IsZero(M[i][j])) {allZeros[i] = false; break;}
         }
         if (allZeros[i]) {nonZeroRowCount--;}
      }
      if(nonZeroRowCount == rowCount) {return;}
      double[][] newM = new double[nonZeroRowCount][colCount];
      int newRowIndex = 0;
      for (int i=0; i<rowCount; i++){
         if(!allZeros[i]){
            for (int j=0; j<colCount; j++){
               newM[newRowIndex][j] = M[i][j];
            }
            newRowIndex++;
         }
      }
      M = newM;
      ResetLengths();
   }
   private boolean IsZero(double toTestForBeingZero){
      //may want to redefine this to include near-zero values too, so this function
      //is provided for updating purposes
      if (toTestForBeingZero == 0) {return true;}
      return false;
   }
   private int NonZeroRow(int columnToFindNonZeroRowFor){
      for (int i=0; i<colCount; i++){
         if (!IsZero(M[i][columnToFindNonZeroRowFor])){return i;}
      }
      return -1;
   }
   public void PrimitiveRoundingErrorCleanUp(){
      for (int i=0; i<rowCount; i++){
         for (int j=0; j<colCount; j++){
            if(Math.abs(M[i][j])<postNormalizationRoundingErrorThreshold){M[i][j] = 0.0;}
         }
      }
   }
   public void Invert(){
      if (colCount != rowCount) {return;}
      Matrix temp = new Matrix(rowCount, 2 * colCount);
      for (int i=0; i<rowCount; i++){
         for (int j=0;   j<colCount; j++){
            temp.M[i][j] = M[i][j];
            temp.M[i][colCount+j] = 0.0;
         }
         temp.M[i][colCount+i] = 1.0;
      }
      
      temp.SimpleGaussJordanElimination();
      
      for (int i=0; i<rowCount; i++){
         for (int j=0;   j<colCount; j++){
            M[i][j] = temp.M[i][colCount+j];
         }
      }
   }
   
   public void Add(Matrix toAdd){
      int toAddRowCount=(rowCount<toAdd.rowCount)?rowCount:toAdd.rowCount, toAddColCount=(colCount<toAdd.colCount)?colCount:toAdd.colCount;
      for (int i=0; i<toAddRowCount; i++){
         for (int j=0; j<toAddColCount; j++){
            M[i][j] += toAdd.M[i][j];
         }
      }
   }
   public static Matrix Add(Matrix toAdd1, Matrix toAdd2){
      int toAddRowCount=(toAdd1.rowCount<toAdd2.rowCount)?toAdd1.rowCount:toAdd2.rowCount, toAddColCount=(toAdd1.colCount<toAdd2.colCount)?toAdd1.colCount:toAdd2.colCount;
      Matrix toReturn = new Matrix(toAddRowCount, toAddColCount);
      for (int i=0; i<toAddRowCount; i++){
         for (int j=0; j<toAddColCount; j++){
            toReturn.M[i][j] = toAdd1.M[i][j] + toAdd2.M[i][j];
         }
      }
      return toReturn;
   }
   public void Subtract(Matrix toAdd){
      int toAddRowCount=(rowCount<toAdd.rowCount)?rowCount:toAdd.rowCount, toAddColCount=(colCount<toAdd.colCount)?colCount:toAdd.colCount;
      for (int i=0; i<toAddRowCount; i++){
         for (int j=0; j<toAddColCount; j++){
            M[i][j] -= toAdd.M[i][j];
         }
      }
   }
   public static Matrix Subtract(Matrix toAdd1, Matrix toAdd2){
      int toAddRowCount=(toAdd1.rowCount<toAdd2.rowCount)?toAdd1.rowCount:toAdd2.rowCount, toAddColCount=(toAdd1.colCount<toAdd2.colCount)?toAdd1.colCount:toAdd2.colCount;
      Matrix toReturn = new Matrix(toAddRowCount, toAddColCount);
      for (int i=0; i<toAddRowCount; i++){
         for (int j=0; j<toAddColCount; j++){
            toReturn.M[i][j] = toAdd1.M[i][j] - toAdd2.M[i][j];
         }
      }
      return toReturn;
   }
   public void Multiply(Matrix toMult){
      if (colCount!=toMult.rowCount){return;}
      double[][] temp = new double[rowCount][toMult.colCount];
      for (int i=0; i<rowCount; i++){
         for (int j=0; j<toMult.colCount; j++){
            for (int k=0; k<colCount; k++){
               temp[i][j] += M[i][k] * toMult.M[k][j];
            }
         }
      }
      M = temp;
      ResetLengths();
   }
   public static Matrix Multiply(Matrix toMult1, Matrix toMult2){
      if (toMult1.colCount!=toMult2.rowCount){return new Matrix(new double[][] {});}
      Matrix toReturn = new Matrix(toMult1.rowCount, toMult2.colCount);
      for (int i=0; i<toMult1.rowCount; i++){
         for (int j=0; j<toMult2.colCount; j++){
            toReturn.M[i][j] = 0.0;
            for (int k=0; k<toMult1.colCount; k++){
               toReturn.M[i][j] += toMult1.M[i][k] * toMult2.M[k][j];
            }
         }
      }
      return toReturn;
   }
   public void Multiply(double scaler){
      for (int i=0; i<rowCount; i++){
         for (int j=0; j<colCount; j++){
            M[i][j] *= scaler;
         }
      }
   }
   public static Matrix Multiply(double scaler, Matrix matrix){
      Matrix toReturn = new Matrix(matrix.rowCount, matrix.colCount);
      for (int i=0; i<matrix.rowCount; i++){
         for (int j=0; j<matrix.colCount; j++){
            toReturn.M[i][j] = scaler * matrix.M[i][j];
         }
      }   
      return toReturn;
   }

   public static Matrix Identity (int length){
      Matrix toReturn = new Matrix(length, length);
      for (int i=0; i<length; i++){
         for (int j=0; j<length; j++){
            toReturn.M[i][j] = (i==j)?1:0;
         }
      }
      return toReturn;
   }
   public void Transpose(){
      double[][] temp = new double[colCount][rowCount];
      for (int i=0; i<rowCount; i++) {
         for (int j=0; j<rowCount; j++) {
            temp[j][i] = M[i][j];
         }
      }
      M = temp;
      ResetLengths();
   }
   public static Matrix Transpose(Matrix toTranspose){
      Matrix toReturn = new Matrix(toTranspose.colCount, toTranspose.rowCount);
      for (int i=0; i<toTranspose.rowCount; i++) {
         for (int j=0; j<toTranspose.rowCount; j++) {
            toReturn.M[j][i] = toTranspose.M[i][j];
         }
      }
      return toReturn;
   }
   private void ResetLengths(){
      rowCount = M.length;
      colCount = M[0].length;
   }
   public void SimpleGaussJordanElimination(){
      //Normalize();
      GeneralGaussJordanElimination(1);
      Sort();
      //Normalize();
      PrimitiveRoundingErrorCleanUp();
   }
   public void GeneralGaussJordanElimination(int countOfColumnsOnTheRightToNotUseInElimination){
      double ratio, maxValue, thisValue;
      int columnsToEliminateWith = (colCount - countOfColumnsOnTheRightToNotUseInElimination<rowCount)?colCount - countOfColumnsOnTheRightToNotUseInElimination:rowCount;
      int maxIndex;
      boolean ledToByZeros;
      //EliminateZeroRows();

      //normalize
      Normalize(countOfColumnsOnTheRightToNotUseInElimination);
      PrimitiveRoundingErrorCleanUp();
      
      for (int i=0; i<columnsToEliminateWith; i++){
         //Print("Before iteration #" + i);
         //find max magnitude in column
         maxIndex = -1;
         maxValue = 0.0;
         for (int j=0; j<rowCount; j++){
            ledToByZeros = true;
            for (int k=0; k<i; k++){
               if(!IsZero(M[j][k])){ledToByZeros = false; break;}
            }
            if (!ledToByZeros) {continue;}
            thisValue = Math.abs(M[j][i]);
            if (thisValue > maxValue){
               maxIndex = j;
               maxValue = thisValue;
            }
         }

         //skip this iteration if the max is a zero
         if (IsZero(maxValue) || maxIndex < 0){continue;}
         
         //eliminate based on this row
         for (int j=0; j<rowCount; j++){
            if (j==maxIndex) {continue;}
            ratio = M[j][i] / M[maxIndex][i];
            for (int k=0; k<colCount; k++){
               M[j][k] -= ratio * M[maxIndex][k];
            }
         }
         //normalize
         Normalize(countOfColumnsOnTheRightToNotUseInElimination);
         PrimitiveRoundingErrorCleanUp();
      }
      //Print("Post-elimination");
   }
   public void Sort(){
      int columnToSortTo = (colCount < rowCount) ? colCount:rowCount, maxIndex;
      double maxValue, thisValue;
      for (int i=0; i<columnToSortTo; i++){
         maxIndex = i;
         maxValue = Math.abs(M[i][i]);
         for (int j=i+1; j<rowCount; j++){
            thisValue = Math.abs(M[j][i]);
            if (thisValue > maxValue){
               maxIndex = j;
               maxValue = thisValue;
            }
         }
         if(maxIndex>i){SwapRows(i, maxIndex);}
      }
   }
   public void SwapRows (int firstRowIndex, int secondRowIndex){
      double temp;
      for (int i=0; i<colCount; i++){
         temp = M[firstRowIndex][i];
         M[firstRowIndex][i] = M[secondRowIndex][i];
         M[secondRowIndex][i] = temp;
      }
   }
   public void GaussJordanEliminationOld(){
      double ratio;
      
      EliminateZeroRows();

      //NonZeroRow(i)
      int thisRow;
      /*for (int i=0; i<colCount; i++){
         thisRow = NonZeroRow(i);
         if (thisRow == -1) {continue;}
         for (int j=0; j<rowCount; j++){
            if (j != thisRow){
               ratio = M[j][i] / M[thisRow][i];
               for (int k=0; k<colCount; k++){
                  M[j][k] -= ratio * M[thisRow][k];
               }
            }
         }
         EliminateZeroRows();
         Print("After iteration #" + i);
      }*/
      //eliminate
      for (int k=0; k<rowCount && k<colCount; k++){
         for (int i=0; i<rowCount; i++){
            if (i!=k){
               if (i<colCount){
                  ratio = M[i][k] / M[k][k];
                  for (int j=0  ; j<colCount; j++) {M[i][j] -= ratio*M[k][j];}
               } else {
                  //add further elimination for non-square matrices
               }
            }
         }
         EliminateZeroRows();
      }
      
      //EliminateZeroRows();
      Normalize(1); //dunno, didn't require an argument back when this was made
      //old normalization function has been obsoleted and replaced
      //it used to just normalize based on the diagonals
   }
}


Graph.java
Code: Select all
import java.math.BigDecimal;
import java.math.MathContext;


public class Graph {
   int bottomIndex, topIndex, leftIndex, rightIndex;
   int functionCount, leftLabelLength;
   double minX, maxX, minF, maxF;
   double setMinX, setMaxX, setMinF, setMaxF;
   boolean useCustomXDomain, useCustomFDomain;
   PrintSpace graph, legend;  //legend is separate for now
   String[] functions;
   char[] markers;
   char verticalTick = '-';
   char horizontalTick = '|';
   private static final String legendTitle = "Legend";
   
   double[][][] data;  //data[function index][point index][0 for x, 1 for f]
   
   public Graph(int height, int width){
      leftLabelLength = 8;
      //bottomIndex = maxBottom - 3;
      //topIndex = 1;
      //leftIndex = 3;
      //rightIndex = maxRight - 1;
      graph = new PrintSpace(height, width);
      functionCount = 0;
      functions = new String[0];
      markers = new char[0];
      data = new double[0][][];
   }
   public void UseCustomXWindow(double minX, double maxX){
      this.setMinX = minX;
      this.setMaxX = maxX;
      useCustomXDomain = true;
   }
   public void UseCustomFWindow(double minF, double maxF){
      this.setMinF = minF;
      this.setMaxF = maxF;
      useCustomFDomain = true;
   }
   public void UseAutoXWindow(){
      useCustomXDomain = false;
   }
   public void UseAutoFWindow(){
      useCustomFDomain = false;
   }
   public void Add(double[] x, double[] f, char marker, String functionName){
      functionCount++;
      AddFunctionName(functionName);
      AddFunctionData(x, f);
      AddMarker(marker);
   }
   private void AddFunctionName(String toAdd){
      String[] temp = new String[functions.length + 1];
      for (int i=0; i<functions.length; i++){temp[i] = functions[i];}
      temp[functions.length] = toAdd;
      functions = temp;
   }
   private void AddFunctionData(double[] x, double[] f){
      double[][][] temp = new double[data.length+1][][];
      int toAddLength = (x.length<f.length)?x.length:f.length;
      double[][] temp2 = new double[toAddLength][2];
      for (int i=0; i<data.length; i++){temp[i] = data[i];}
      for (int i=0; i<toAddLength; i++){
         temp2[i][0] = x[i];
         temp2[i][1] = f[i];
      }
      temp[data.length] = temp2;
      data = temp;
   }
   private void AddMarker(char marker){
      char[] temp = new char[markers.length+1];
      for (int i=0; i<markers.length; i++){temp[i] = markers[i];}
      temp[markers.length] = marker;
      markers = temp;
   }
   private void GetExtremes(){
      boolean bothSet = false;
      if(useCustomXDomain){
         minX = setMinX;
         maxX = setMaxX;
         bothSet = true;
      } else {
         minX=0.0;
         maxX=0.0;
      }
      if(useCustomFDomain){
         minF = setMinF;
         maxF = setMaxF;
         if (bothSet) {return;}
      } else {
         minF=0.0;
         maxF=0.0;
      }
      
      boolean firstFound = false;
      //set initial values (searches data for a value; there may be empty data sets and don't want ArrayOutOfBounds!)
      for (int i=0; i<data.length; i++){
         for (int j=0; j<data[i].length; j++){
            firstFound = true;
            if(!useCustomXDomain){minX = maxX = data[i][j][0];}
            if(!useCustomFDomain){minF = maxF = data[i][j][1];}
            break;
         }
         if (firstFound) break;
      }
      if (!firstFound) {return;}
      
      //look for actual mins and maxs
      for (int i=0; i<data.length; i++){
         for (int j=0; j<data[i].length; j++){
            if(!useCustomXDomain){
               if (data[i][j][0]<minX) {minX = data[i][j][0];}
               if (data[i][j][0]>maxX) {maxX = data[i][j][0];}
            }
            if(!useCustomFDomain){
               if (data[i][j][1]<minF) {minF = data[i][j][1];} 
               if (data[i][j][1]>maxF) {maxF = data[i][j][1];}
            }
         }
      }
   }
   private void AddPointsToGraph(){
      if(minX == maxX) {return;}
      if(minF == maxF) {return;}
      int yToAdd, xToAdd;
      //System.out.println(minX + "," + maxX + "," + minF + "," + maxF);
      for (int i=0; i<data.length; i++){
         for (int j=0; j<data[i].length; j++){
            //System.out.println(ToGraphIndexY(data[i][j][1], minF, maxF));
            //graph.Add(markers[i], ToGraphIndexX(data[i][j][0], minX, maxX), ToGraphIndexY(data[i][j][1], minF, maxF), i);
            yToAdd = ToGraphIndexY(data[i][j][1], minF, maxF);
            xToAdd = ToGraphIndexX(data[i][j][0], minX, maxX);
            if(yToAdd>bottomIndex||yToAdd<topIndex){continue;}
            if(xToAdd>rightIndex||xToAdd<leftIndex){continue;}
            graph.Add(markers[i], yToAdd, xToAdd, i);
            //ToGraphIndexX(data[i][j][0], minX, maxX);
            //ToGraphIndexY(data[i][j][1], minF, maxF);
         }
      }
   }
   private void AddFrame(){
      //Set box coordinates
      bottomIndex = graph.height - 3;
      topIndex = 0;
      leftIndex = leftLabelLength + 1;
      rightIndex = graph.width - 3;
      
      //Box
      graph.FloatLine(topIndex, leftIndex, bottomIndex, leftIndex);
      graph.FloatLine(topIndex, leftIndex, topIndex, rightIndex);
      graph.FloatLine(topIndex, rightIndex, bottomIndex, rightIndex);
      graph.FloatLine(bottomIndex, leftIndex, bottomIndex, rightIndex);
      graph.Resolve(0);
      
      //x-axis ticks
      graph.Add(horizontalTick, bottomIndex+1, leftIndex, 0);
      graph.Add(horizontalTick, bottomIndex+1, rightIndex, 0);
      
      //y-axis ticks
      graph.Add(verticalTick, topIndex, leftIndex-1, 0);
      graph.Add(verticalTick, bottomIndex, leftIndex-1, 0);

      //x-axis labels
      graph.AddTextCenterAlign(DDBottomLabel(minX), bottomIndex+2, leftIndex,  0);
      graph.AddTextCenterAlign(DDBottomLabel(maxX), bottomIndex+2, rightIndex, 0);
      
      //y-axis labels
      graph.AddTextLeftAlign(DDLeftLabel(maxF), topIndex, 0, 0);
      graph.AddTextLeftAlign(DDLeftLabel(minF), bottomIndex, 0, 0);
   }
   public void Print(String title){
      System.out.println(title + ":");
      Print();
   }
   public void Print(){
      GetExtremes();
      AddFrame();
      AddPointsToGraph();
      graph.Print();
   }
   /*private int ToGraphIndexX(double x, double minX, double maxX){
      System.out.println("f: " + (leftIndex + (int)Math.round(((double)(rightIndex-leftIndex))*(x-minX)/(maxX-minX))));
      return leftIndex + (int)Math.round(((double)(rightIndex-leftIndex))*(x-minX)/(maxX-minX));
   }
   private int ToGraphIndexY(double y, double minY, double maxY){
      System.out.println("x: " + (bottomIndex + (int)Math.round(((double)(topIndex-bottomIndex))*(y-minY)/(maxY-minY))));
      return bottomIndex + (int)Math.round(((double)(topIndex-bottomIndex))*(y-minY)/(maxY-minY));
   }*/
   private int ToGraphIndexX(double x, double minX, double maxX){
      //System.out.println("x: " + (leftIndex + (int)Math.round(((double)(rightIndex-leftIndex))*(x-minX)/(maxX-minX))));
      return leftIndex + (int)Math.round((double)(rightIndex-leftIndex)*(x-minX)/(maxX-minX));
   }
   private int ToGraphIndexY(double y, double minY, double maxY){
      //System.out.println("Y: " + (bottomIndex + (int)Math.round(((double)(topIndex-bottomIndex))*(y-minY)/(maxY-minY))));
      return bottomIndex + (int)Math.round((double)(topIndex-bottomIndex)*(y-minY)/(maxY-minY));
   }
   private String DDLeftLabel (double toDisplay){
      String toReturn;
      if (Double.isNaN(toDisplay)) {
         toReturn = "NaN";
      } else if (Double.isInfinite(toDisplay)){
         toReturn = "Inf";
      } else {
         toReturn = BigDecimal.valueOf(toDisplay).round(new MathContext(leftLabelLength-5)).toString();
      }
      while (toReturn.length() < leftLabelLength) {
         toReturn = " " + toReturn;
      }
      return toReturn;
   }
   private void CreateLegend(){
      if (functions.length < 1) {
         System.out.println("No function names for legend.");
         return;
      }
      int maxStringLength = legendTitle.length();
      int legendHeight, legendWidth;
      for (int i=0; i<functions.length; i++){
         if(functions[i].length() > maxStringLength) {maxStringLength = functions[i].length();}
      }
      legendHeight = functions.length + 4;
      legendWidth = maxStringLength + 7;
      int xOffset = (graph.width-legendWidth)/2;
      if (xOffset<0) {xOffset =0;}
      legend = new PrintSpace(legendHeight, legendWidth+xOffset);
      legend.FloatLine(0, xOffset, 0, legendWidth-1+xOffset);
      legend.FloatLine(0, legendWidth-1+xOffset, legendHeight-1, legendWidth-1+xOffset);
      legend.FloatLine(0, xOffset, legendHeight-1, xOffset);
      legend.FloatLine(legendHeight-1, xOffset, legendHeight-1, legendWidth-1+xOffset);
      legend.Resolve(0);
      legend.FloatLine(2, xOffset, 2, legendWidth-1+xOffset);
      legend.Resolve(0);
      legend.AddTextLeftAlign(legendTitle, 1, 2+xOffset, 0);
      for (int i=0; i<functions.length; i++){
         legend.AddTextLeftAlign(markers[i] + ": " + functions[i], 3+i, 2+xOffset, 0);
      }
   }
   public void PrintLegend(){
      CreateLegend();
      legend.Print();
   }
   private String DDBottomLabel (double toDisplay){
      String toReturn;
      if (Double.isNaN(toDisplay)) {
         toReturn = "NaN";
      } else if (Double.isInfinite(toDisplay)){
         toReturn = "Inf";
      } else {
         toReturn = BigDecimal.valueOf(toDisplay).round(new MathContext(leftLabelLength)).toString();
      }
      while (toReturn.length() < leftLabelLength) {
         toReturn += " ";
         if (toReturn.length() < leftLabelLength) {toReturn = " " + toReturn;}
      }
      return toReturn;
   }
}


See? I even included a few comments!

Seriously though, I set the grad Computational Physics class's curve with code like that. Which makes your point, really. All the professor cared about is that my code worked and did cool little things.

The first time I tutored Software Design, it was.. interesting. Apparently leaving off a few Javadoc comments is enough to get a failing grade on an assignment even when the code works just fine. Then there's the test classes that have to collectively go through every line of code.. ick.

Just so no one feels the need to run it, it just plots a 1-D wavefunction in a potential well over time using an ASCII art chart. It also writes a log file and shows the real and imaginary components, etc., if you flip the correct booleans at the top of the main class, PS6_2.java.
Natural ChemE
Forum Moderator
 
Posts: 1390
Joined: 28 Dec 2009
Location: Existential Desperation, VA
Blog: View Blog (0)


Re: How to Make a PowerPoint-like GUI?

Postby Natural ChemE on May 13th, 2012, 10:59 pm 

kidjan,

Early today I was trying to learn C#, WPF, network programming, and GUI. So I figured that I'd do a combination project and make myself a game of tic-tac-toe in C# using WPF that'd let you play against folks over the net.

The program’s just this. There’s a form with a 3x3 grid on it. Each grid slot holds an Image that can be blank, of an ‘X’, or of an ‘O’. There’s also a TicTacToeBoard class that represents a tic-tac-toe game. Whenever a player clicks a slot on the grid, that click is sent off to the TicTacToeBoard instance, and then the grid’s images are updated with what the TicTacToeBoard instance says that they should show. This part all works great.

Then I added in the networking part. Now two players can play over the network, but since a particular player’s board only updates when he performs a click, he doesn’t see his opponent’s move, if any, until he attempts to move.

To make it so that the board automatically updates when the TicTacToeBoard instance reflects a change, I added an event to the TicTacToeBoard that the GUI form subscribes to. Now when the remote player makes his move, the local player’s board calls the update method. This compiles with no errors in Visual Studio 2010 Pro.

The problem is when you run it and let the remote player make a move. When this happens, the update board method is called but throws an error.

At first it called some error relating to “STAThread”. I haven’t a clue what that’s about, but I tried adding “[STAThread]” infront of every method in the entire project to get it to work at Google’s advice. No luck. Any idea why this might’ve happened?

So anyway, I noted that it was occurring on a line
Code: Select all
Image toAdd = new Image();
. I was using this line to create new images of X’s and O’s and such, then add them to the tac-tac-toe board’s grid. Since it seemed unhappy creating new images, I decided to rewrite the code such that it only modifies the existing images’ source instead. This successfully avoided the weird “STAThread” exception thingie.

I still get an error at pretty much the same spot though. Now instead of adding a new Image to each grid, I have a persistent 3x3 array of images , and I update them using
Code: Select all
tics[i, j].Source = board.image(i, j);
where is the instance of TicTacToeBoard.

The new error is
Visual Studio 2010 wrote:InvalidOperationException occurred
The calling thread cannot access this object because a different thread owns it.


At this point I’d like to note that I have not explicitly created any threads and have no idea what WPF is doing inside its black box. So, erm, how might one fix this?

And, just to note it, not trying to waste your time with a silly game. It’s just that the CAD’s pretty much the same thing as this programmatically – little networking signals and a GUI on the front. I’ve figured out the wrappers and math parts and such, just need to get the darn thing to talk to itself over the cluster and look good (enough to not frighten small children).
Natural ChemE
Forum Moderator
 
Posts: 1390
Joined: 28 Dec 2009
Location: Existential Desperation, VA
Blog: View Blog (0)


Re: How to Make a PowerPoint-like GUI?

Postby kidjan on May 14th, 2012, 9:40 pm 

You probably need to use BeginInvoke. Here's a basic implementation, and if you google it for a bit I'm sure you'll figure out why this is important (hint: it has to do with that whole dedicated GUI thread thing I mentioned above).
User avatar
kidjan
Active Member
 
Posts: 1926
Joined: 25 Jul 2007
Location: Earth.
Blog: View Blog (0)


Re: How to Make a PowerPoint-like GUI?

Postby Natural ChemE on May 15th, 2012, 3:13 pm 

kidjan,

Excellent, thank you!

Alright, so apparently BeginInvoke and such are specific to WinForms, so I can't use them here. But it seems that there are many C# programmers who moved from WinForms to WPF, so Google has plenty of results for translating such things to whatever the WPF equivalent is.

Trying to do some of the background research now. Looks like Threading Model, Windows Presentation Foundation, MSDN will the next step.

So, yup, you were right; it was the main GUI thread issue. Thanks again!

Edit - Nevermind, the link I posted actually says that we can use BeginInvoke(). Gonna have to read up on this a bit more.
Natural ChemE
Forum Moderator
 
Posts: 1390
Joined: 28 Dec 2009
Location: Existential Desperation, VA
Blog: View Blog (0)


Re: How to Make a PowerPoint-like GUI?

Postby kidjan on May 15th, 2012, 7:08 pm 

So basically, there's one thread in charge of updating the GUI, and you need to ask this thread to do the work for you. BeginInvoke is how you do that:

If only one thread can modify the UI, how do background threads interact with the user? A background thread can ask the UI thread to perform an operation on its behalf. It does this by registering a work item with the Dispatcher of the UI thread. The Dispatcher class provides two methods for registering work items: Invoke and BeginInvoke. Both methods schedule a delegate for execution. Invoke is a synchronous call – that is, it doesn’t return until the UI thread actually finishes executing the delegate. BeginInvoke is asynchronous and returns immediately.


So you can use Invoke, but that'll block on the thread you're currently running on--which isn't a bad thing, if this is the behavior you want. You can use BeginInvoke, which basically says "hey UI thread, handle this operation when you get around to it--later", after which it can go off and process more data.

But in a nutshell, you can't (shouldn't) use some random thread to update the UI in .NET; that's the domain of your dedicated UI thread.
User avatar
kidjan
Active Member
 
Posts: 1926
Joined: 25 Jul 2007
Location: Earth.
Blog: View Blog (0)


Re: How to Make a PowerPoint-like GUI?

Postby Natural ChemE on May 16th, 2012, 6:27 pm 

kidjan,

Alright, my program works, and I'm thrilled! The original
Code: Select all
tics[i, j].Source = board.image(i, j);
tics[i, j].Tag = board.ASCII(i, j);
became
Code: Select all
SetImageCallback cb = new SetImageCallback(setImage);
grid.Dispatcher.BeginInvoke(cb, new point(i, j));
with
Code: Select all
private void setImage(point location)
{
    tics[location.x, location.y].Source = board.image(location.x, location.y);
    tics[location.x, location.y].Tag = board.ASCII(location.x, location.y);
}
.

Thanks again!

Edit - Changed "this.Dispatcher" to "grid.Dispatcher". I'm guessing that directly accessing the grid would be faster than routing everything through the form.

Not that speed particularly matters for such a simple program, but the CAD that I'm aiming to replace suffers from some pretty serious GUI lag. Just want to make sure that I avoid as many of its mistakes as possible.
Natural ChemE
Forum Moderator
 
Posts: 1390
Joined: 28 Dec 2009
Location: Existential Desperation, VA
Blog: View Blog (0)


Re: How to Make a PowerPoint-like GUI?

Postby kidjan on May 17th, 2012, 10:06 pm 

Glad to be of help. Let me know if you want to do some open source dev; I'm always looking for fun projects.
User avatar
kidjan
Active Member
 
Posts: 1926
Joined: 25 Jul 2007
Location: Earth.
Blog: View Blog (0)


Re: How to Make a PowerPoint-like GUI?

Postby kidjan on May 21st, 2012, 8:19 pm 

Edit - Changed "this.Dispatcher" to "grid.Dispatcher". I'm guessing that directly accessing the grid would be faster than routing everything through the form.


One more comment: I'm really not sure if it matters. But there's an adage in software engineering that if you didn't measure, then you're almost definitely wasting your time.

By far the best profiler I've used in C# is the ANTS performance profiler. It does cost money, but if you're serious about performance, there is no substitute for measuring.

I'm sure there's some free options, but when you go to optimize I'd recommend using some profiler.
User avatar
kidjan
Active Member
 
Posts: 1926
Joined: 25 Jul 2007
Location: Earth.
Blog: View Blog (0)


Re: How to Make a PowerPoint-like GUI?

Postby Natural ChemE on June 23rd, 2012, 8:41 pm 

kidjan wrote:Glad to be of help. Let me know if you want to do some open source dev; I'm always looking for fun projects.
I'm debating if I should start a company on this one or just open source it.

My original plan was to just create something that would be more useful in my research. But I'm having a lot of success with it, and now I'm developing something that works much better than the standard package - which is very expensive despite painfully inadequate.

Is there any good reason to open source something like a good CAD program?
Natural ChemE
Forum Moderator
 
Posts: 1390
Joined: 28 Dec 2009
Location: Existential Desperation, VA
Blog: View Blog (0)


Re: How to Make a PowerPoint-like GUI?

Postby kidjan on June 28th, 2012, 2:23 pm 

The decision to open source really depends on your business model. It is possible to have a profitable open source project, but you invariably assume the role of support, maintenance, training, etc. And you also have to weigh what other options people have, how much it would cost them to write it themselves, how valuable it is, etc.

If it's core intellectual property, then I tend to lean towards "do not open source." If it's something that you'd benefit from other people using and/or contributing to, and it's not some core piece of intellectual property in your business, then open sourcing it makes sense--this way, you get maintenance, bug fixing, features, etc. added by a community around your product (in theory, anyway).

Dunno; hard to say for certain. You'd have to give me more specifics.
User avatar
kidjan
Active Member
 
Posts: 1926
Joined: 25 Jul 2007
Location: Earth.
Blog: View Blog (0)


Re: How to Make a PowerPoint-like GUI?

Postby Natural ChemE on August 14th, 2012, 2:35 am 

kidjan,

As a side project, I've put together a tutoring program that walks new users through using the standard process engineering CAD (as opposed to the CAD that I was making in this thread).

In short, the training program lets the user pick a lesson. Once the lesson is picked, the training program loads up that lesson's guide and starts an instance of the CAD with its GUI showing. The training programs repeatedly reads the CAD to see what the user's done (sorta like the spell check does to text in Word), then shows the next step for the user to take in a little always-on-top window on the side.

This part's working well, and now I'd like to add to it.

See, while my little program gives a step-by-step "How To" guide for a particular lesson, the little window that presents this information can't hold something like a Power Point slide that gives the user more detailed information on demand. For example, say that the lesson is showing the user how to fill out something theoretically complicated, like picking out a thermodynamic model for the chemical system. Picking out such a model in the CAD isn’t hard at all; there’s just a little drop-down menu box to select from. However I’d like to show a little PowerPoint-slide-like window that shows the user a diagram that’ll remind them of some of the important underlying equations behind the model to be selected. Or for another example, say that a lesson is showing the user how to pick a mathematical convergence method. Again this is a simple process with a simple instruction, but I’d like to give the user an optional detailed viewing area that’ll give them conceptual information (i.e., like a quick animation of the secant method, or a link to Wikipedia’s article on the Broyden method).

So my question here is: what's the best way to store/render that little detailed explanation area?

My first thoughts are:
(1) use an HTML browser area and have the detailed explanation as an *.html file;
(2) somehow use a PPT viewer and have the detailed explanation as a *.pptx file;
(3) just have that little area show a picture of some sort and use a *.png or other image file;
(4) add WPF markup to my lesson plan XTML files and have that detailed explanation area be a customized WPF window.

While I imagine that all of these approaches would work (except for (2), as I haven't checked into an embeddable PPT viewer yet), I'm having trouble guessing which would be best to use.

An important point in this selection is that I’d like it to be easy for my fellow grad students to generate this sort of file. We use a lot of PowerPoint, and PowerPoint can hold everything from animations to hyperlinks, so I’m sort of leaning in that direction if it’d be advisable (and possible!).

EDIT: Looks like embedding a PowerPoint is possible. Then, I guess, is it the best route?

Just incase it's relevant for background (nothing below this point is necessary):
The training program lists all of the lesson files in its Lessons directory. Each lesson file has a header that describes what the lesson is, and this is passed along to the user. The lesson files themselves use a crude XTML format that I cooked up for this purpose.

Once the user selects a lesson, the training program opens an instance of the CAD program from the starting point simulation file specified by the lesson file. Then the training program parses the steps necessary to complete that simulation from the lesson file.

Then the training program, accessing the CAD through ActiveX, continuously scans the CAD like a word processor scans the text when spell checking. The training programs starts by scanning the things that should be done first (like specifying what units the simulation will use, etc.), and when it finds that something is correctly specified, it moves on to the next check step. As soon as it finds something that isn't correctly specified, it lists correctly specifying that thing to be the next step for the user to do.

After a scan is complete, the scanning thread sleeps for some set time (about 250ms right now), then begins another scan. New instructions to the user are reported to a small, always-on-top instruction window. The lesson is complete once a scan finds that the user has done everything correctly.

In certain cases, such as when filling out the units to be used during the very first introductory lesson, I may want another viewing area to pop up with more than just the instructions necessary to fill in the units. You know, to give the user tips or present some mathematical idea or something. It's the medium for this secondary viewing area that I'm trying to figure out, whether it should be based on HTML, or just an image file, or PowerPoint, etc.
Natural ChemE
Forum Moderator
 
Posts: 1390
Joined: 28 Dec 2009
Location: Existential Desperation, VA
Blog: View Blog (0)


Re: How to Make a PowerPoint-like GUI?

Postby Charityst021 on February 5th, 2013, 10:13 am 

You may have accidentally changed your Windows theme. Right-click over the desktop and select 'Personalize', select 'Windows 7' from the Aero themes and it should be set back to the normal settings.
Charityst021
Forum Neophyte
 
Posts: 1
Joined: 15 Nov 2012
Blog: View Blog (0)



Return to Computers

Who is online

Users browsing this forum: Heritrix [Crawler] and 1 guest