3025996b |
1 | // Copyright (C) 2006 Erik Dahlberg |
2 | // |
6e40d5eb |
3 | // This file is part of LSystem3D. |
3025996b |
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 | |
6e40d5eb |
22 | #include <sstream> |
3025996b |
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" |
6e40d5eb |
31 | #include "rule.h" |
32 | #include "ruleset.h" |
33 | |
34 | using namespace std; |
3025996b |
35 | |
36 | |
37 | |
38 | // message map |
39 | FXDEFMAP(GUI) GUIMap[] = |
40 | { |
6e40d5eb |
41 | FXMAPFUNC(SEL_COMMAND, GUI::ID_GENERATE, GUI::onGenerateButtonPressed), |
42 | FXMAPFUNC(SEL_COMMAND, GUI::ID_HELP, GUI::onHelpButtonPressed) |
3025996b |
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 | |
6e40d5eb |
62 | |
3025996b |
63 | // create the widgets |
64 | |
65 | // the main frame |
66 | FXVerticalFrame *mainFrame = new FXVerticalFrame(this, LAYOUT_FILL_X); |
67 | |
6e40d5eb |
68 | // axiom text field |
3025996b |
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); |
6e40d5eb |
72 | |
73 | // rules multiline text field |
3025996b |
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); |
6e40d5eb |
79 | |
80 | // angle spinner |
3025996b |
81 | FXHorizontalFrame *angleFrame = new FXHorizontalFrame(mainFrame); |
82 | new FXLabel(angleFrame, "Angle:"); |
83 | _angleRealSpinner = new FXRealSpinner(angleFrame, 5); |
6e40d5eb |
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 |
3025996b |
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 | |
6e40d5eb |
97 | // diameter factor spinner |
3025996b |
98 | new FXLabel(diameterFrame, "Factor:"); |
99 | _diameterFactorRealSpinner = new FXRealSpinner(diameterFrame, 5); |
6e40d5eb |
100 | _diameterFactorRealSpinner->setValue(0.9); |
101 | _diameterFactorRealSpinner->setIncrement(0.01); |
3025996b |
102 | |
103 | // separator |
104 | new FXHorizontalSeparator(mainFrame, SEPARATOR_RIDGE | LAYOUT_FILL_X); |
105 | |
6e40d5eb |
106 | // generate button |
3025996b |
107 | new FXButton(mainFrame, "&Generate", NULL, this, GUI::ID_GENERATE); |
108 | |
6e40d5eb |
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 | |
3025996b |
148 | |
149 | |
150 | // load L-system parameters from file and sync with GUI |
151 | |
152 | _lsystemParameters.load(_lsystem, "lsystem.xml"); |
153 | |
154 | // axiom |
6e40d5eb |
155 | _axiomTextField->setText(_lsystem->getAxiom().getContent().c_str()); |
3025996b |
156 | |
157 | // rules |
6e40d5eb |
158 | rulemap rules = _lsystem->getRules().getRules(); |
159 | string rulesString; |
160 | for (rulemap::iterator it = rules.begin(); it != rules.end(); ++it) |
3025996b |
161 | { |
6e40d5eb |
162 | rulesString += it->second.toString(); |
163 | rulesString += '\n'; |
3025996b |
164 | } |
6e40d5eb |
165 | _rulesText->setText(rulesString.c_str(), rulesString.size()); |
3025996b |
166 | |
167 | // angle |
168 | _angleRealSpinner->setValue(_lsystem->getAngle()); |
169 | |
6e40d5eb |
170 | // depth |
171 | _depthSpinner->setValue(_lsystem->getDepth()); |
3025996b |
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 | /** |
6e40d5eb |
189 | * Called by the system when the "Generate" button is pressed |
3025996b |
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 | |
6e40d5eb |
201 | // get user input form widgets |
3025996b |
202 | |
203 | // axiom |
6e40d5eb |
204 | Rule axiom("axiom", _axiomTextField->getText().text(), 1.0); |
205 | _lsystem->setAxiom(axiom); |
3025996b |
206 | |
207 | // rules |
208 | string ruleSet = _rulesText->getText().text(); |
6e40d5eb |
209 | string currentRule; |
3025996b |
210 | for (int i = 0; i < ruleSet.size(); i++) |
211 | { |
6e40d5eb |
212 | if (ruleSet[i] != '\n') |
3025996b |
213 | { |
6e40d5eb |
214 | // rule component |
3025996b |
215 | currentRule += ruleSet[i]; |
6e40d5eb |
216 | |
217 | if (i == ruleSet.size() - 1) |
218 | { |
219 | // last char in whole rule string |
220 | _lsystem->setRule(Rule(currentRule)); |
221 | currentRule.clear(); |
222 | } |
3025996b |
223 | } |
6e40d5eb |
224 | else |
3025996b |
225 | { |
6e40d5eb |
226 | // last char in current rule |
227 | _lsystem->setRule(Rule(currentRule)); |
3025996b |
228 | currentRule.clear(); |
3025996b |
229 | } |
230 | } |
231 | |
232 | // angle |
233 | _lsystem->setAngle(_angleRealSpinner->getValue()); |
234 | |
6e40d5eb |
235 | // depth |
236 | _lsystem->setDepth(_depthSpinner->getValue()); |
3025996b |
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 | } |
6e40d5eb |
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 | } |