1 // Copyright (C) 2006 Erik Dahlberg
3 // This file is part of LSystem3D.
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.
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.
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
28 #include "lindenmayersystem.h"
29 #include "lsystemparameters.h"
30 #include "renderingsurface.h"
39 FXDEFMAP(GUI
) GUIMap
[] =
41 FXMAPFUNC(SEL_COMMAND
, GUI::ID_GENERATE
, GUI::onGenerate
),
42 FXMAPFUNC(SEL_COMMAND
, GUI::ID_LOAD
, GUI::onLoad
),
43 FXMAPFUNC(SEL_COMMAND
, GUI::ID_SAVE
, GUI::onSave
),
44 FXMAPFUNC(SEL_COMMAND
, GUI::ID_QUIT
, GUI::onQuit
),
45 FXMAPFUNC(SEL_COMMAND
, GUI::ID_HELP_USAGE
, GUI::onHelpUsage
),
46 FXMAPFUNC(SEL_COMMAND
, GUI::ID_HELP_RULES
, GUI::onHelpRules
),
47 FXMAPFUNC(SEL_CLOSE
, 0, GUI::onQuit
)
50 // macro to set up class implementation
51 FXIMPLEMENT(GUI
, FXMainWindow
, GUIMap
, ARRAYNUMBER(GUIMap
))
57 * @param application the application object
58 * @param renderingSurface the rendering surface
59 * @param lsystem the Lindenmayer system generator
61 GUI::GUI(FXApp
*application
, RenderingSurface
*renderingSurface
, LindenmayerSystem
*lsystem
) : FXMainWindow(application
, "LSystem3D" , NULL
, NULL
, DECOR_ALL
, 920, 100, 0, 0)
63 _renderingSurface
= renderingSurface
;
71 FXMenuBar
*menubar
= new FXMenuBar(this, LAYOUT_FILL_X
);
74 _fileMenuPane
= new FXMenuPane(this);
75 new FXMenuTitle(menubar
, "&File", NULL
, _fileMenuPane
);
76 new FXMenuCommand(_fileMenuPane
, "&Load...", NULL
, this, GUI::ID_LOAD
);
77 new FXMenuCommand(_fileMenuPane
, "&Save...", NULL
, this, GUI::ID_SAVE
);
78 new FXMenuCommand(_fileMenuPane
, "&Quit", NULL
, this, GUI::ID_QUIT
);
81 _helpMenuPane
= new FXMenuPane(this);
82 new FXMenuTitle(menubar
, "&Help", NULL
, _helpMenuPane
);
83 new FXMenuCommand(_helpMenuPane
, "&Usage", NULL
, this, GUI::ID_HELP_USAGE
);
84 new FXMenuCommand(_helpMenuPane
, "&Rules", NULL
, this, GUI::ID_HELP_RULES
);
88 FXVerticalFrame
*mainFrame
= new FXVerticalFrame(this, LAYOUT_FILL_X
);
92 FXHorizontalFrame
*axiomFrame
= new FXHorizontalFrame(mainFrame
, LAYOUT_FILL_X
);
93 new FXLabel(axiomFrame
, "Axiom:");
94 _axiomTextField
= new FXTextField(axiomFrame
, 10, NULL
, 0, LAYOUT_FILL_X
);
96 // rules multiline text field
97 FXHorizontalFrame
*rulesFrame
= new FXHorizontalFrame(mainFrame
, LAYOUT_FILL_X
);
98 new FXLabel(rulesFrame
, "Rules:");
99 _rulesText
= new FXText(rulesFrame
, NULL
, 0, LAYOUT_FILL_X
);
100 _rulesText
->setVisibleColumns(30);
101 _rulesText
->setVisibleRows(10);
104 FXHorizontalFrame
*angleFrame
= new FXHorizontalFrame(mainFrame
);
105 new FXLabel(angleFrame
, "Angle:");
106 _angleRealSpinner
= new FXRealSpinner(angleFrame
, 5);
109 FXHorizontalFrame
*depthFrame
= new FXHorizontalFrame(mainFrame
);
110 new FXLabel(depthFrame
, "Depth:");
111 _depthSpinner
= new FXSpinner(depthFrame
, 5);
114 FXHorizontalFrame
*diameterFrame
= new FXHorizontalFrame(mainFrame
);
115 new FXLabel(diameterFrame
, "Diameter:");
116 _diameterRealSpinner
= new FXRealSpinner(diameterFrame
, 5);
117 _diameterRealSpinner
->setValue(0.1);
118 _diameterRealSpinner
->setIncrement(0.01);
120 // diameter factor spinner
121 new FXLabel(diameterFrame
, "Factor:");
122 _diameterFactorRealSpinner
= new FXRealSpinner(diameterFrame
, 5);
123 _diameterFactorRealSpinner
->setValue(1.0);
124 _diameterFactorRealSpinner
->setIncrement(0.01);
128 new FXHorizontalSeparator(mainFrame
, SEPARATOR_RIDGE
| LAYOUT_FILL_X
);
132 new FXButton(mainFrame
, "&Generate", NULL
, this, GUI::ID_GENERATE
);
136 stringstream usageHelpText
;
137 usageHelpText
<< "Rendering window:" << endl
138 << "Left mouse button: Move model in z-plane" << endl
139 << "Middle mouse button: Zoom in/out" << endl
140 << "Right mouse button: Rotate around y-axis" << endl
142 << "Controller window:" << endl
143 << "Axiom: Initial rule" << endl
144 << "Rules: The L-system rules" << endl
145 << "Angle: Turn/pitch/roll angle" << endl
146 << "Depth: Depth of recursion" << endl
147 << "Diameter: Initial diameter of segment" << endl
148 << "Factor: For each recursion level, multiply segment diameter with this value" << endl
149 << "Generate: Generate the L-system";
151 _helpUsageMessageBox
= new FXMessageBox(getApp(), "Usage", usageHelpText
.str().c_str(), NULL
, MBOX_OK
);
155 stringstream rulesHelpText
;
156 rulesHelpText
<< "Rules:" << endl
157 << "F : Walk forward, creating a segment" << endl
158 << "A-Z : Replacement rule" << endl
159 << "A(0.33) : Probability factor 0.33 for rule \"A\"" << endl
160 << "= : Rule assignment" << endl
161 << "+ : Turn left" << endl
162 << "- : Turn right" << endl
163 << "&& : Pitch down" << endl
164 << "^ : Pitch up" << endl
165 << "\\ : Roll left" << endl
166 << "/ : Roll right" << endl
167 << "| : Turn around 180 degrees" << endl
168 << "[ : Save state to stack" << endl
169 << "] : Load state from stack" << endl
170 << "{ : Create a planar surface" << endl
171 << "} : Close a planar surface" << endl
172 << "f : One vertex in a planar surface, specified CCW" << endl
173 << "! : Decrement segment diameter" << endl
174 << "\' : Increment current index to color table" << endl
175 << ", : Decrement current index to color table" << endl
177 << "Example:" << endl
178 << "F(0.33)=[+FL]F/[-FL]F!" << endl
179 << "F(0.33)=F[&&FL]F!" << endl
180 << "F(0.34)=F/[-FL]&&F!" << endl
181 << "L={,-f++f-|-f++f-'}";
183 _helpRulesMessageBox
= new FXMessageBox(getApp(), "Rules", rulesHelpText
.str().c_str(), NULL
, MBOX_OK
);
186 loadLSystem("lsystem.xml");
196 delete _fileMenuPane
;
197 delete _helpMenuPane
;
203 * Create and initialize the window
207 // create the window and make it appear
208 FXMainWindow::create();
215 * Called by the system when the "Generate" button is pressed
216 * @param sender the sender object
217 * @param selector message type and id
218 * @param data event related data
221 long GUI::onGenerate(FXObject
*sender
, FXSelector selector
, void *data
)
227 // get user input form widgets
230 Rule
axiom("axiom", _axiomTextField
->getText().text(), 1.0);
231 _lsystem
->setAxiom(axiom
);
234 string ruleSet
= _rulesText
->getText().text();
236 for (int i
= 0; i
< ruleSet
.size(); i
++)
238 if (ruleSet
[i
] != '\n')
241 currentRule
+= ruleSet
[i
];
243 if (i
== ruleSet
.size() - 1)
245 // last char in whole rule string
246 _lsystem
->setRule(Rule(currentRule
));
252 // last char in current rule
253 _lsystem
->setRule(Rule(currentRule
));
259 _lsystem
->setAngle(_angleRealSpinner
->getValue());
262 _lsystem
->setDepth(_depthSpinner
->getValue());
265 _lsystem
->setDiameter(_diameterRealSpinner
->getValue());
268 _lsystem
->setDiameterFactor(_diameterFactorRealSpinner
->getValue());
271 // generate and render L-system to screen
272 _lsystem
->generate();
273 _renderingSurface
->draw();
279 * Called by the system when the File->Load menu command is selected
280 * @param sender the sender object
281 * @param selector message type and id
282 * @param data event related data
285 long GUI::onLoad(FXObject
*sender
, FXSelector selector
, void *data
)
287 FXString loadFilename
= FXFileDialog::getOpenFilename(this, "Load file...", ".");
289 if (!loadFilename
.empty())
292 loadLSystem(loadFilename
.text());
299 * Called by the system when the File->Save menu command is selected
300 * @param sender the sender object
301 * @param selector message type and id
302 * @param data event related data
305 long GUI::onSave(FXObject
*sender
, FXSelector selector
, void *data
)
307 FXString saveFilename
= FXFileDialog::getSaveFilename(this, "Save file...", ".");
309 if (FXStat::exists(saveFilename
))
311 // the file already exists
313 if (MBOX_CLICKED_YES
== FXMessageBox::question(this, MBOX_YES_NO
, "Overwrite file", "Overwrite existing file?"))
316 _lsystemParameters
.save(_lsystem
, saveFilename
.text());
322 _lsystemParameters
.save(_lsystem
, saveFilename
.text());
329 * Called by the system when the close button or the File->Quit command item is selected
330 * @param sender the sender object
331 * @param selector message type and id
332 * @param data event related data
335 long GUI::onQuit(FXObject
*sender
, FXSelector selector
, void *data
)
345 * Called by the system when the Help->Usage menu command is selected
346 * @param sender the sender object
347 * @param selector message type and id
348 * @param data event related data
351 long GUI::onHelpUsage(FXObject
*sender
, FXSelector selector
, void *data
)
353 _helpUsageMessageBox
->show(PLACEMENT_OWNER
);
359 * Called by the system when the Help->Rules menu command is selected
360 * @param sender the sender object
361 * @param selector message type and id
362 * @param data event related data
365 long GUI::onHelpRules(FXObject
*sender
, FXSelector selector
, void *data
)
367 _helpRulesMessageBox
->show(PLACEMENT_OWNER
);
373 * Load L-system from file and sync with GUI
374 * @param filename path of the L-system file
376 void GUI::loadLSystem(string filename
)
379 _lsystemParameters
.load(_lsystem
, filename
);
385 _axiomTextField
->setText(_lsystem
->getAxiom().getContent().c_str());
388 rulemap rules
= _lsystem
->getRules().getRules();
390 for (rulemap::iterator it
= rules
.begin(); it
!= rules
.end(); ++it
)
392 rulesString
+= it
->second
.toString();
395 _rulesText
->setText(rulesString
.c_str(), rulesString
.size());
398 _angleRealSpinner
->setValue(_lsystem
->getAngle());
401 _depthSpinner
->setValue(_lsystem
->getDepth());
404 _diameterRealSpinner
->setValue(_lsystem
->getDiameter());
406 // segment diameter factor
407 _diameterFactorRealSpinner
->setValue(_lsystem
->getDiameterFactor());