1a8ca9e6af2ed058ea11bdf9f3c4204e2fb0b7c2
[lsystem3d.git] / src / gui.cpp
1 // Copyright (C) 2006 Erik Dahlberg
2 //
3 // This file is part of LSystem3D.
4 //
5 // LSystem3D is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License
7 // as published by the Free Software Foundation; either version 2
8 // of the License, or (at your option) any later version.
9 //
10 // LSystem3D is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with LSystem3D; if not, write to the Free Software
17 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18
19
20
21
22 #include <sstream>
23 #include <string>
24
25 #include "fx.h"
26
27 #include "gui.h"
28 #include "lindenmayersystem.h"
29 #include "lsystemparameters.h"
30 #include "renderingsurface.h"
31 #include "rule.h"
32 #include "ruleset.h"
33
34 using namespace std;
35
36
37
38 // message map
39 FXDEFMAP(GUI) GUIMap[] =
40 {
41 FXMAPFUNC(SEL_COMMAND, GUI::ID_GENERATE, GUI::onGenerateButtonPressed),
42 FXMAPFUNC(SEL_COMMAND, GUI::ID_HELP, GUI::onHelpButtonPressed)
43 };
44
45 // macro to set up class implementation
46 FXIMPLEMENT(GUI, FXMainWindow, GUIMap, ARRAYNUMBER(GUIMap))
47
48
49
50 /**
51 * Constructor
52 * @param application the FOX application object
53 * @param renderingSurface the rendering surface
54 * @param lsystem the Lindenmayer-system
55 */
56 GUI::GUI(FXApp *application, RenderingSurface *renderingSurface, LindenmayerSystem *lsystem) : FXMainWindow(application, "LSystem3D" , NULL, NULL, DECOR_ALL, 920, 100, 0, 0)
57 {
58 _lsystem = lsystem;
59 _renderingSurface = renderingSurface;
60
61
62
63 // create the widgets
64
65 // the main frame
66 FXVerticalFrame *mainFrame = new FXVerticalFrame(this, LAYOUT_FILL_X);
67
68 // axiom text field
69 FXHorizontalFrame *axiomFrame = new FXHorizontalFrame(mainFrame, LAYOUT_FILL_X);
70 new FXLabel(axiomFrame, "Axiom:");
71 _axiomTextField = new FXTextField(axiomFrame, 10, NULL, 0, LAYOUT_FILL_X);
72
73 // rules multiline text field
74 FXHorizontalFrame *rulesFrame = new FXHorizontalFrame(mainFrame, LAYOUT_FILL_X);
75 new FXLabel(rulesFrame, "Rules:");
76 _rulesText = new FXText(rulesFrame, NULL, 0, LAYOUT_FILL_X);
77 _rulesText->setVisibleColumns(30);
78 _rulesText->setVisibleRows(10);
79
80 // angle spinner
81 FXHorizontalFrame *angleFrame = new FXHorizontalFrame(mainFrame);
82 new FXLabel(angleFrame, "Angle:");
83 _angleRealSpinner = new FXRealSpinner(angleFrame, 5);
84
85 // depth spinner
86 FXHorizontalFrame *depthFrame = new FXHorizontalFrame(mainFrame);
87 new FXLabel(depthFrame, "Depth:");
88 _depthSpinner = new FXSpinner(depthFrame, 5);
89
90 // diameter spinner
91 FXHorizontalFrame *diameterFrame = new FXHorizontalFrame(mainFrame);
92 new FXLabel(diameterFrame, "Diameter:");
93 _diameterRealSpinner = new FXRealSpinner(diameterFrame, 5);
94 _diameterRealSpinner->setValue(0.2);
95 _diameterRealSpinner->setIncrement(0.1);
96
97 // diameter factor spinner
98 new FXLabel(diameterFrame, "Factor:");
99 _diameterFactorRealSpinner = new FXRealSpinner(diameterFrame, 5);
100 _diameterFactorRealSpinner->setValue(0.9);
101 _diameterFactorRealSpinner->setIncrement(0.01);
102
103 // separator
104 new FXHorizontalSeparator(mainFrame, SEPARATOR_RIDGE | LAYOUT_FILL_X);
105
106 // generate button
107 new FXButton(mainFrame, "&Generate", NULL, this, GUI::ID_GENERATE);
108
109 // help button
110 new FXButton(mainFrame, "&Help", NULL, this, GUI::ID_HELP);
111
112 // help dialog
113 stringstream helpText;
114 helpText << "Mouse:" << endl
115 << "Left button: Move model in z-plane" << endl
116 << "Middle button: Zoom in/out" << endl
117 << "Right button: Rotate around y-axis" << endl
118 << endl
119 << "Rules:" << endl
120 << "F : Walk forward, leaving a trace" << endl
121 << "A-Z : Replacement rule" << endl
122 << "A(0.33) : Probability factor 0.33 for rule \"A\"" << endl
123 << "= : Rule assignment" << endl
124 << "+ : Turn left" << endl
125 << "- : Turn right" << endl
126 << "&& : Pitch down" << endl
127 << "^ : Pitch up" << endl
128 << "\\ : Roll left" << endl
129 << "/ : Roll right" << endl
130 << "| : Turn around 180 degrees" << endl
131 << "[ : Save state to stack" << endl
132 << "] : Load state from stack" << endl
133 << "{ : Create a planar surface" << endl
134 << "} : Close a planar surface" << endl
135 << "f : One vertex in a planar surface, specified CCW" << endl
136 << "! : Decrement diameter of segment" << endl
137 << "\' : Increment current index to color table" << endl
138 << ", : Decrement current index to color table" << endl
139 << endl
140 << "Example:" << endl
141 << "F(0.33)=[+F/L]/F[-F&&L]F!" << endl
142 << "F(0.33)=F[+F+L]&&F!" << endl
143 << "F(0.34)=F[-F-L]/F!" << endl
144 << "L=,{-f++f-|-f++f-}'";
145
146 _helpMessageBox = new FXMessageBox(getApp(), "Help", helpText.str().c_str(), NULL, MBOX_OK);
147
148
149
150 // load L-system parameters from file and sync with GUI
151
152 _lsystemParameters.load(_lsystem, "lsystem.xml");
153
154 // axiom
155 _axiomTextField->setText(_lsystem->getAxiom().getContent().c_str());
156
157 // rules
158 rulemap rules = _lsystem->getRules().getRules();
159 string rulesString;
160 for (rulemap::iterator it = rules.begin(); it != rules.end(); ++it)
161 {
162 rulesString += it->second.toString();
163 rulesString += '\n';
164 }
165 _rulesText->setText(rulesString.c_str(), rulesString.size());
166
167 // angle
168 _angleRealSpinner->setValue(_lsystem->getAngle());
169
170 // depth
171 _depthSpinner->setValue(_lsystem->getDepth());
172 }
173
174
175
176 /**
177 * Create and initialize the window
178 */
179 void GUI::create()
180 {
181 // create the window and make it appear
182 FXMainWindow::create();
183 show();
184 }
185
186
187
188 /**
189 * Called by the system when the "Generate" button is pressed
190 * @param sender the sender object
191 * @param selector message type and id
192 * @param data event related data
193 * @return
194 */
195 long GUI::onGenerateButtonPressed(FXObject *sender, FXSelector selector, void *data)
196 {
197 // clear L-system
198 _lsystem->clear();
199
200
201 // get user input form widgets
202
203 // axiom
204 Rule axiom("axiom", _axiomTextField->getText().text(), 1.0);
205 _lsystem->setAxiom(axiom);
206
207 // rules
208 string ruleSet = _rulesText->getText().text();
209 string currentRule;
210 for (int i = 0; i < ruleSet.size(); i++)
211 {
212 if (ruleSet[i] != '\n')
213 {
214 // rule component
215 currentRule += ruleSet[i];
216
217 if (i == ruleSet.size() - 1)
218 {
219 // last char in whole rule string
220 _lsystem->setRule(Rule(currentRule));
221 currentRule.clear();
222 }
223 }
224 else
225 {
226 // last char in current rule
227 _lsystem->setRule(Rule(currentRule));
228 currentRule.clear();
229 }
230 }
231
232 // angle
233 _lsystem->setAngle(_angleRealSpinner->getValue());
234
235 // depth
236 _lsystem->setDepth(_depthSpinner->getValue());
237
238 // diameter
239 _lsystem->setDiameter(_diameterRealSpinner->getValue());
240
241 // diameter factor
242 _lsystem->setDiameterFactor(_diameterFactorRealSpinner->getValue());
243
244
245 // generate and render L-system to screen
246 _lsystem->generate();
247 _renderingSurface->draw();
248 }
249
250
251
252 /**
253 * Called by the system when the "Help" button is pressed
254 * @param sender the sender object
255 * @param selector message type and id
256 * @param data event related data
257 * @return
258 */
259 long GUI::onHelpButtonPressed(FXObject *sender, FXSelector selector, void *data)
260 {
261 _helpMessageBox->show(PLACEMENT_OWNER);
262 }