| 1 | /* w_graph.c |
| 2 | * |
| 3 | * Micropolis, Unix Version. This game was released for the Unix platform |
| 4 | * in or about 1990 and has been modified for inclusion in the One Laptop |
| 5 | * Per Child program. Copyright (C) 1989 - 2007 Electronic Arts Inc. If |
| 6 | * you need assistance with this program, you may contact: |
| 7 | * http://wiki.laptop.org/go/Micropolis or email micropolis@laptop.org. |
| 8 | * |
| 9 | * This program is free software: you can redistribute it and/or modify |
| 10 | * it under the terms of the GNU General Public License as published by |
| 11 | * the Free Software Foundation, either version 3 of the License, or (at |
| 12 | * your option) any later version. |
| 13 | * |
| 14 | * This program is distributed in the hope that it will be useful, but |
| 15 | * WITHOUT ANY WARRANTY; without even the implied warranty of |
| 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| 17 | * General Public License for more details. You should have received a |
| 18 | * copy of the GNU General Public License along with this program. If |
| 19 | * not, see <http://www.gnu.org/licenses/>. |
| 20 | * |
| 21 | * ADDITIONAL TERMS per GNU GPL Section 7 |
| 22 | * |
| 23 | * No trademark or publicity rights are granted. This license does NOT |
| 24 | * give you any right, title or interest in the trademark SimCity or any |
| 25 | * other Electronic Arts trademark. You may not distribute any |
| 26 | * modification of this program using the trademark SimCity or claim any |
| 27 | * affliation or association with Electronic Arts Inc. or its employees. |
| 28 | * |
| 29 | * Any propagation or conveyance of this program must include this |
| 30 | * copyright notice and these terms. |
| 31 | * |
| 32 | * If you convey this program (or any modifications of it) and assume |
| 33 | * contractual liability for the program to recipients of it, you agree |
| 34 | * to indemnify Electronic Arts for any liability that those contractual |
| 35 | * assumptions impose on Electronic Arts. |
| 36 | * |
| 37 | * You may not misrepresent the origins of this program; modified |
| 38 | * versions of the program must be marked as such and not identified as |
| 39 | * the original program. |
| 40 | * |
| 41 | * This disclaimer supplements the one included in the General Public |
| 42 | * License. TO THE FULLEST EXTENT PERMISSIBLE UNDER APPLICABLE LAW, THIS |
| 43 | * PROGRAM IS PROVIDED TO YOU "AS IS," WITH ALL FAULTS, WITHOUT WARRANTY |
| 44 | * OF ANY KIND, AND YOUR USE IS AT YOUR SOLE RISK. THE ENTIRE RISK OF |
| 45 | * SATISFACTORY QUALITY AND PERFORMANCE RESIDES WITH YOU. ELECTRONIC ARTS |
| 46 | * DISCLAIMS ANY AND ALL EXPRESS, IMPLIED OR STATUTORY WARRANTIES, |
| 47 | * INCLUDING IMPLIED WARRANTIES OF MERCHANTABILITY, SATISFACTORY QUALITY, |
| 48 | * FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT OF THIRD PARTY |
| 49 | * RIGHTS, AND WARRANTIES (IF ANY) ARISING FROM A COURSE OF DEALING, |
| 50 | * USAGE, OR TRADE PRACTICE. ELECTRONIC ARTS DOES NOT WARRANT AGAINST |
| 51 | * INTERFERENCE WITH YOUR ENJOYMENT OF THE PROGRAM; THAT THE PROGRAM WILL |
| 52 | * MEET YOUR REQUIREMENTS; THAT OPERATION OF THE PROGRAM WILL BE |
| 53 | * UNINTERRUPTED OR ERROR-FREE, OR THAT THE PROGRAM WILL BE COMPATIBLE |
| 54 | * WITH THIRD PARTY SOFTWARE OR THAT ANY ERRORS IN THE PROGRAM WILL BE |
| 55 | * CORRECTED. NO ORAL OR WRITTEN ADVICE PROVIDED BY ELECTRONIC ARTS OR |
| 56 | * ANY AUTHORIZED REPRESENTATIVE SHALL CREATE A WARRANTY. SOME |
| 57 | * JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF OR LIMITATIONS ON IMPLIED |
| 58 | * WARRANTIES OR THE LIMITATIONS ON THE APPLICABLE STATUTORY RIGHTS OF A |
| 59 | * CONSUMER, SO SOME OR ALL OF THE ABOVE EXCLUSIONS AND LIMITATIONS MAY |
| 60 | * NOT APPLY TO YOU. |
| 61 | */ |
| 62 | #include "sim.h" |
| 63 | |
| 64 | |
| 65 | short NewGraph = 0; |
| 66 | short AllMax; |
| 67 | unsigned char *History10[HISTORIES]; |
| 68 | unsigned char *History120[HISTORIES]; |
| 69 | int HistoryInitialized = 0; |
| 70 | short Graph10Max, Graph120Max; |
| 71 | Tcl_HashTable GraphCmds; |
| 72 | int GraphUpdateTime = 100; |
| 73 | |
| 74 | |
| 75 | #define DEF_GRAPH_FONT "-Adobe-Helvetica-Bold-R-Normal-*-140-*" |
| 76 | #define DEF_GRAPH_BG_COLOR "#b0b0b0" |
| 77 | #define DEF_GRAPH_BG_MONO "#ffffff" |
| 78 | #define DEF_GRAPH_BORDER_WIDTH "0" |
| 79 | #define DEF_GRAPH_RELIEF "flat" |
| 80 | |
| 81 | Tk_ConfigSpec GraphConfigSpecs[] = { |
| 82 | {TK_CONFIG_FONT, "-font", (char *) NULL, (char *) NULL, |
| 83 | DEF_GRAPH_FONT, Tk_Offset(SimGraph, fontPtr), 0}, |
| 84 | {TK_CONFIG_BORDER, "-background", "background", "Background", |
| 85 | DEF_GRAPH_BG_COLOR, Tk_Offset(SimGraph, border), |
| 86 | TK_CONFIG_COLOR_ONLY}, |
| 87 | {TK_CONFIG_BORDER, "-background", "background", "Background", |
| 88 | DEF_GRAPH_BG_MONO, Tk_Offset(SimGraph, border), |
| 89 | TK_CONFIG_MONO_ONLY}, |
| 90 | {TK_CONFIG_PIXELS, "-borderwidth", "borderWidth", "BorderWidth", |
| 91 | DEF_GRAPH_BORDER_WIDTH, Tk_Offset(SimGraph, borderWidth), 0}, |
| 92 | {TK_CONFIG_RELIEF, "-relief", "relief", "Relief", |
| 93 | DEF_GRAPH_RELIEF, Tk_Offset(SimGraph, relief), 0}, |
| 94 | {TK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL, |
| 95 | (char *) NULL, 0, 0} |
| 96 | }; |
| 97 | |
| 98 | |
| 99 | XDisplay *FindXDisplay(); |
| 100 | |
| 101 | |
| 102 | static void |
| 103 | DisplaySimGraph(ClientData clientData) |
| 104 | { |
| 105 | SimGraph *graph = (SimGraph *) clientData; |
| 106 | Tk_Window tkwin = graph->tkwin; |
| 107 | Pixmap pm = None; |
| 108 | Drawable d; |
| 109 | |
| 110 | graph->flags &= ~VIEW_REDRAW_PENDING; |
| 111 | |
| 112 | //fprintf(stderr, "DisplaySimGraph token %d\n", graph->draw_graph_token); |
| 113 | |
| 114 | assert(graph->draw_graph_token != 0); |
| 115 | |
| 116 | if (graph->draw_graph_token != 0) { |
| 117 | // Tk_DeleteTimerHandler(graph->draw_graph_token); |
| 118 | graph->draw_graph_token = 0; |
| 119 | } |
| 120 | |
| 121 | if (graph->visible && (tkwin != NULL) && Tk_IsMapped(tkwin)) { |
| 122 | DoUpdateGraph(graph); |
| 123 | } |
| 124 | } |
| 125 | |
| 126 | |
| 127 | void |
| 128 | DestroySimGraph(ClientData clientData) |
| 129 | { |
| 130 | SimGraph *graph = (SimGraph *) clientData; |
| 131 | |
| 132 | DestroyGraph(graph); |
| 133 | } |
| 134 | |
| 135 | |
| 136 | EventuallyRedrawGraph(SimGraph *graph) |
| 137 | { |
| 138 | if (!(graph->flags & VIEW_REDRAW_PENDING)) { |
| 139 | assert(graph->draw_graph_token == 0); |
| 140 | if (graph->draw_graph_token == 0) { |
| 141 | graph->draw_graph_token = |
| 142 | Tk_CreateTimerHandler( |
| 143 | GraphUpdateTime, |
| 144 | DisplaySimGraph, |
| 145 | (ClientData) graph); |
| 146 | graph->flags |= VIEW_REDRAW_PENDING; |
| 147 | //fprintf(stderr, "EventuallyRedrawGraph token %d\n", graph->draw_graph_token); |
| 148 | } |
| 149 | } |
| 150 | } |
| 151 | |
| 152 | |
| 153 | void |
| 154 | SimGraphEventProc(ClientData clientData, XEvent *eventPtr) |
| 155 | { |
| 156 | SimGraph *graph = (SimGraph *) clientData; |
| 157 | |
| 158 | if ((eventPtr->type == Expose) && (eventPtr->xexpose.count == 0)) { |
| 159 | graph->visible = 1; |
| 160 | EventuallyRedrawGraph(graph); |
| 161 | } else if (eventPtr->type == MapNotify) { |
| 162 | graph->visible = 1; |
| 163 | } else if (eventPtr->type == UnmapNotify) { |
| 164 | graph->visible = 0; |
| 165 | } else if (eventPtr->type == VisibilityNotify) { |
| 166 | if (eventPtr->xvisibility.state == VisibilityFullyObscured) |
| 167 | graph->visible = 0; |
| 168 | else |
| 169 | graph->visible = 1; |
| 170 | } else if (eventPtr->type == ConfigureNotify) { |
| 171 | DoResizeGraph(graph, |
| 172 | eventPtr->xconfigure.width, |
| 173 | eventPtr->xconfigure.height); |
| 174 | EventuallyRedrawGraph(graph); |
| 175 | } else if (eventPtr->type == DestroyNotify) { |
| 176 | Tcl_DeleteCommand(graph->interp, Tk_PathName(graph->tkwin)); |
| 177 | graph->tkwin = NULL; |
| 178 | if (graph->flags & VIEW_REDRAW_PENDING) { |
| 179 | //fprintf(stderr, "SimGraphEventProc Destroy token %d\n", graph->draw_graph_token); |
| 180 | assert(graph->draw_graph_token != 0); |
| 181 | if (graph->draw_graph_token != 0) { |
| 182 | Tk_DeleteTimerHandler(graph->draw_graph_token); |
| 183 | graph->draw_graph_token = 0; |
| 184 | } |
| 185 | graph->flags &= ~VIEW_REDRAW_PENDING; |
| 186 | } |
| 187 | Tk_EventuallyFree((ClientData) graph, DestroySimGraph); |
| 188 | } |
| 189 | } |
| 190 | |
| 191 | |
| 192 | int GraphCmdconfigure(GRAPH_ARGS) |
| 193 | { |
| 194 | int result = TCL_OK; |
| 195 | |
| 196 | if (argc == 2) { |
| 197 | result = Tk_ConfigureInfo(interp, graph->tkwin, GraphConfigSpecs, |
| 198 | (char *) graph, (char *) NULL, 0); |
| 199 | } else if (argc == 3) { |
| 200 | result = Tk_ConfigureInfo(interp, graph->tkwin, GraphConfigSpecs, |
| 201 | (char *) graph, argv[2], 0); |
| 202 | } else { |
| 203 | result = ConfigureSimGraph(interp, graph, argc-2, argv+2, |
| 204 | TK_CONFIG_ARGV_ONLY); |
| 205 | } |
| 206 | return TCL_OK; |
| 207 | } |
| 208 | |
| 209 | |
| 210 | int GraphCmdposition(GRAPH_ARGS) |
| 211 | { |
| 212 | int result = TCL_OK; |
| 213 | |
| 214 | if ((argc != 2) && (argc != 4)) { |
| 215 | return TCL_ERROR; |
| 216 | } |
| 217 | if (argc == 4) { |
| 218 | if ((Tcl_GetInt(interp, argv[2], &graph->w_x) != TCL_OK) |
| 219 | || (Tcl_GetInt(interp, argv[3], &graph->w_y) != TCL_OK)) { |
| 220 | return TCL_ERROR; |
| 221 | } |
| 222 | } |
| 223 | sprintf(interp->result, "%d %d", graph->w_x, graph->w_y); |
| 224 | return TCL_OK; |
| 225 | } |
| 226 | |
| 227 | |
| 228 | int GraphCmdsize(GRAPH_ARGS) |
| 229 | { |
| 230 | if ((argc != 2) && (argc != 4)) { |
| 231 | return TCL_ERROR; |
| 232 | } |
| 233 | if (argc == 4) { |
| 234 | int w, h; |
| 235 | |
| 236 | if (Tcl_GetInt(interp, argv[2], &w) != TCL_OK) { |
| 237 | return TCL_ERROR; |
| 238 | } |
| 239 | if (Tcl_GetInt(interp, argv[3], &h) != TCL_OK) { |
| 240 | return TCL_ERROR; |
| 241 | } |
| 242 | graph->w_width = w; |
| 243 | graph->w_height = h; |
| 244 | } |
| 245 | sprintf(interp->result, "%d %d", graph->w_width, graph->w_height); |
| 246 | return TCL_OK; |
| 247 | } |
| 248 | |
| 249 | |
| 250 | int GraphCmdVisible(GRAPH_ARGS) |
| 251 | { |
| 252 | int visible; |
| 253 | |
| 254 | if ((argc != 2) && (argc != 3)) { |
| 255 | Tcl_AppendResult(interp, "wrong # args", (char *) NULL); |
| 256 | return TCL_ERROR; |
| 257 | } |
| 258 | |
| 259 | if (argc == 3) { |
| 260 | if ((Tcl_GetInt(interp, argv[2], &visible) != TCL_OK) || |
| 261 | (visible < 0) || (visible > 1)) { |
| 262 | Tcl_AppendResult(interp, " bogus args", (char *) NULL); |
| 263 | return TCL_ERROR; |
| 264 | } |
| 265 | |
| 266 | graph->visible = visible; |
| 267 | } |
| 268 | |
| 269 | sprintf(interp->result, "%d", graph->visible); |
| 270 | |
| 271 | return TCL_OK; |
| 272 | } |
| 273 | |
| 274 | |
| 275 | int GraphCmdRange(GRAPH_ARGS) |
| 276 | { |
| 277 | int range; |
| 278 | |
| 279 | if ((argc != 2) && (argc != 3)) { |
| 280 | Tcl_AppendResult(interp, "wrong # args", (char *) NULL); |
| 281 | return TCL_ERROR; |
| 282 | } |
| 283 | |
| 284 | if (argc == 3) { |
| 285 | if ((Tcl_GetInt(interp, argv[2], &range) != TCL_OK) || |
| 286 | ((range != 10) && (range != 120))) { |
| 287 | Tcl_AppendResult(interp, " bogus args", (char *) NULL); |
| 288 | return TCL_ERROR; |
| 289 | } |
| 290 | |
| 291 | graph->range = range; |
| 292 | NewGraph = 1; |
| 293 | } |
| 294 | |
| 295 | sprintf(interp->result, "%d", graph->range); |
| 296 | |
| 297 | return TCL_OK; |
| 298 | } |
| 299 | |
| 300 | |
| 301 | int GraphCmdMask(GRAPH_ARGS) |
| 302 | { |
| 303 | int mask; |
| 304 | |
| 305 | if ((argc != 2) && (argc != 3)) { |
| 306 | Tcl_AppendResult(interp, "wrong # args", (char *) NULL); |
| 307 | return TCL_ERROR; |
| 308 | } |
| 309 | |
| 310 | if (argc == 3) { |
| 311 | if ((Tcl_GetInt(interp, argv[2], &mask) != TCL_OK) || |
| 312 | (mask < 0) || (mask > 63)) { |
| 313 | Tcl_AppendResult(interp, " bogus args", (char *) NULL); |
| 314 | return TCL_ERROR; |
| 315 | } |
| 316 | |
| 317 | graph->mask = mask; |
| 318 | NewGraph = 1; |
| 319 | } |
| 320 | |
| 321 | sprintf(interp->result, "%d", graph->mask); |
| 322 | |
| 323 | return TCL_OK; |
| 324 | } |
| 325 | |
| 326 | |
| 327 | int |
| 328 | DoGraphCmd(CLIENT_ARGS) |
| 329 | { |
| 330 | SimGraph *graph = (SimGraph *) clientData; |
| 331 | Tcl_HashEntry *ent; |
| 332 | int result = TCL_OK; |
| 333 | int (*cmd)(); |
| 334 | |
| 335 | if (argc < 2) { |
| 336 | return TCL_ERROR; |
| 337 | } |
| 338 | |
| 339 | if (ent = Tcl_FindHashEntry(&GraphCmds, argv[1])) { |
| 340 | cmd = (int (*)())ent->clientData; |
| 341 | Tk_Preserve((ClientData) graph); |
| 342 | result = cmd(graph, interp, argc, argv); |
| 343 | Tk_Release((ClientData) graph); |
| 344 | } else { |
| 345 | Tcl_AppendResult(interp, "unknown command name: \"", |
| 346 | argv[0], " ", argv[1], "\".", (char *) NULL); |
| 347 | result = TCL_ERROR; |
| 348 | } |
| 349 | return result; |
| 350 | } |
| 351 | |
| 352 | |
| 353 | int |
| 354 | GraphViewCmd(CLIENT_ARGS) |
| 355 | { |
| 356 | SimGraph *graph; |
| 357 | Tk_Window tkwin = (Tk_Window) clientData; |
| 358 | |
| 359 | if (argc < 2) { |
| 360 | Tcl_AppendResult(interp, "wrong # args: should be \"", |
| 361 | argv[0], " pathName ?options?\"", (char *) NULL); |
| 362 | return TCL_ERROR; |
| 363 | } |
| 364 | |
| 365 | tkwin = Tk_CreateWindowFromPath(interp, tkwin, |
| 366 | argv[1], (char *) NULL); |
| 367 | if (tkwin == NULL) { |
| 368 | return TCL_ERROR; |
| 369 | } |
| 370 | |
| 371 | graph = (SimGraph *)ckalloc(sizeof (SimGraph)); |
| 372 | |
| 373 | graph->tkwin = tkwin; |
| 374 | graph->interp = interp; |
| 375 | graph->flags = 0; |
| 376 | |
| 377 | Tk_SetClass(graph->tkwin, "GraphView"); |
| 378 | Tk_CreateEventHandler(graph->tkwin, |
| 379 | VisibilityChangeMask | |
| 380 | ExposureMask | |
| 381 | StructureNotifyMask, |
| 382 | SimGraphEventProc, (ClientData) graph); |
| 383 | Tcl_CreateCommand(interp, Tk_PathName(graph->tkwin), |
| 384 | DoGraphCmd, (ClientData) graph, (void (*)()) NULL); |
| 385 | |
| 386 | /* |
| 387 | Tk_MakeWindowExist(graph->tkwin); |
| 388 | */ |
| 389 | |
| 390 | if (getenv("XSYNCHRONIZE") != NULL) { |
| 391 | XSynchronize(Tk_Display(tkwin), 1); |
| 392 | } |
| 393 | |
| 394 | InitNewGraph(graph); |
| 395 | DoNewGraph(graph); |
| 396 | |
| 397 | if (ConfigureSimGraph(interp, graph, argc-2, argv+2, 0) != TCL_OK) { |
| 398 | /* XXX: destroy graph */ |
| 399 | Tk_DestroyWindow(graph->tkwin); |
| 400 | return TCL_ERROR; |
| 401 | } |
| 402 | |
| 403 | interp->result = Tk_PathName(graph->tkwin); |
| 404 | return TCL_OK; |
| 405 | } |
| 406 | |
| 407 | |
| 408 | int |
| 409 | ConfigureSimGraph(Tcl_Interp *interp, SimGraph *graph, |
| 410 | int argc, char **argv, int flags) |
| 411 | { |
| 412 | if (Tk_ConfigureWidget(interp, graph->tkwin, GraphConfigSpecs, |
| 413 | argc, argv, (char *) graph, flags) != TCL_OK) { |
| 414 | return TCL_ERROR; |
| 415 | } |
| 416 | |
| 417 | Tk_SetBackgroundFromBorder(graph->tkwin, graph->border); |
| 418 | |
| 419 | EventuallyRedrawGraph(graph); |
| 420 | return TCL_OK; |
| 421 | } |
| 422 | |
| 423 | |
| 424 | |
| 425 | |
| 426 | char *HistName[] = { |
| 427 | "Residential", "Commercial", "Industrial", |
| 428 | "Cash Flow", "Crime", "Pollution" |
| 429 | }; |
| 430 | |
| 431 | unsigned char HistColor[] = { |
| 432 | COLOR_LIGHTGREEN, COLOR_DARKBLUE, COLOR_YELLOW, |
| 433 | COLOR_DARKGREEN, COLOR_RED, COLOR_OLIVE |
| 434 | }; |
| 435 | |
| 436 | |
| 437 | graph_command_init() |
| 438 | { |
| 439 | int new; |
| 440 | |
| 441 | Tcl_CreateCommand(tk_mainInterp, "graphview", GraphViewCmd, |
| 442 | (ClientData)MainWindow, (void (*)()) NULL); |
| 443 | |
| 444 | Tcl_InitHashTable(&GraphCmds, TCL_STRING_KEYS); |
| 445 | |
| 446 | #define GRAPH_CMD(name) HASHED_CMD(Graph, name) |
| 447 | |
| 448 | GRAPH_CMD(configure); |
| 449 | GRAPH_CMD(position); |
| 450 | GRAPH_CMD(size); |
| 451 | GRAPH_CMD(Visible); |
| 452 | GRAPH_CMD(Range); |
| 453 | GRAPH_CMD(Mask); |
| 454 | } |
| 455 | |
| 456 | |
| 457 | |
| 458 | void |
| 459 | drawMonth(short *hist, unsigned char *s, float scale) |
| 460 | { |
| 461 | register short val; |
| 462 | register short x; |
| 463 | |
| 464 | for (x = 0; x < 120; x++) { |
| 465 | val = hist[x] * scale; |
| 466 | if (val < 0) val = 0; |
| 467 | if (val > 255) val = 255; |
| 468 | s[119 - x] = val; |
| 469 | } |
| 470 | } |
| 471 | |
| 472 | |
| 473 | void |
| 474 | doAllGraphs(void) |
| 475 | { |
| 476 | float scaleValue; |
| 477 | |
| 478 | AllMax = 0; |
| 479 | if (ResHisMax > AllMax) AllMax = ResHisMax; |
| 480 | if (ComHisMax > AllMax) AllMax = ComHisMax; |
| 481 | if (IndHisMax > AllMax) AllMax = IndHisMax; |
| 482 | if (AllMax <= 128) AllMax = 0; |
| 483 | |
| 484 | if (AllMax) { |
| 485 | scaleValue = 128.0 / AllMax; |
| 486 | } else { |
| 487 | scaleValue = 1.0; |
| 488 | } |
| 489 | |
| 490 | // scaleValue = 0.5; // XXX |
| 491 | |
| 492 | drawMonth(ResHis, History10[RES_HIST], scaleValue); |
| 493 | drawMonth(ComHis, History10[COM_HIST], scaleValue); |
| 494 | drawMonth(IndHis, History10[IND_HIST], scaleValue); |
| 495 | drawMonth(MoneyHis, History10[MONEY_HIST], 1.0); |
| 496 | drawMonth(CrimeHis, History10[CRIME_HIST], 1.0); |
| 497 | drawMonth(PollutionHis, History10[POLLUTION_HIST], 1.0); |
| 498 | |
| 499 | AllMax = 0; |
| 500 | if (Res2HisMax > AllMax) AllMax = Res2HisMax; |
| 501 | if (Com2HisMax > AllMax) AllMax = Com2HisMax; |
| 502 | if (Ind2HisMax > AllMax) AllMax = Ind2HisMax; |
| 503 | if (AllMax <= 128) AllMax = 0; |
| 504 | |
| 505 | if (AllMax) { |
| 506 | scaleValue = 128.0 / AllMax; |
| 507 | } else { |
| 508 | scaleValue = 1.0; |
| 509 | } |
| 510 | |
| 511 | // scaleValue = 0.5; // XXX |
| 512 | |
| 513 | drawMonth(ResHis + 120, History120[RES_HIST], scaleValue); |
| 514 | drawMonth(ComHis + 120, History120[COM_HIST], scaleValue); |
| 515 | drawMonth(IndHis + 120, History120[IND_HIST], scaleValue); |
| 516 | drawMonth(MoneyHis + 120, History120[MONEY_HIST], 1.0); |
| 517 | drawMonth(CrimeHis + 120, History120[CRIME_HIST], 1.0); |
| 518 | drawMonth(PollutionHis + 120, History120[POLLUTION_HIST], 1.0); |
| 519 | } |
| 520 | |
| 521 | |
| 522 | void |
| 523 | ChangeCensus(void) |
| 524 | { |
| 525 | CensusChanged = 1; |
| 526 | } |
| 527 | |
| 528 | |
| 529 | void |
| 530 | graphDoer(void) |
| 531 | { |
| 532 | SimGraph *graph; |
| 533 | |
| 534 | if (CensusChanged) { |
| 535 | doAllGraphs(); |
| 536 | NewGraph = 1; |
| 537 | CensusChanged = 0; |
| 538 | } |
| 539 | |
| 540 | if (NewGraph) { |
| 541 | for (graph = sim->graph; graph != NULL; graph = graph->next) { |
| 542 | EventuallyRedrawGraph(graph); |
| 543 | } |
| 544 | NewGraph = 0; |
| 545 | } |
| 546 | } |
| 547 | |
| 548 | |
| 549 | void |
| 550 | initGraphs(void) |
| 551 | { |
| 552 | int i; |
| 553 | SimGraph *graph; |
| 554 | |
| 555 | for (graph = sim->graph; graph != NULL; graph = graph->next) { |
| 556 | graph->range = 10; |
| 557 | graph->mask = ALL_HISTORIES; |
| 558 | } |
| 559 | |
| 560 | if (!HistoryInitialized) { |
| 561 | HistoryInitialized = 1; |
| 562 | for (i = 0; i < HISTORIES; i++) { |
| 563 | History10[i] = (unsigned char *)ckalloc(120); |
| 564 | History120[i] = (unsigned char *)ckalloc(120); |
| 565 | } |
| 566 | } |
| 567 | } |
| 568 | |
| 569 | |
| 570 | /* comefrom: InitWillStuff */ |
| 571 | InitGraphMax(void) |
| 572 | { |
| 573 | register x; |
| 574 | |
| 575 | ResHisMax = 0; |
| 576 | ComHisMax = 0; |
| 577 | IndHisMax = 0; |
| 578 | for (x = 118; x >= 0; x--) { |
| 579 | if (ResHis[x] > ResHisMax) ResHisMax = ResHis[x]; |
| 580 | if (ComHis[x] > ComHisMax) ComHisMax = ComHis[x]; |
| 581 | if (IndHis[x] > IndHisMax) IndHisMax = IndHis[x]; |
| 582 | if (ResHis[x] < 0) ResHis[x] = 0; |
| 583 | if (ComHis[x] < 0) ComHis[x] = 0; |
| 584 | if (IndHis[x] < 0) IndHis[x] = 0; |
| 585 | } |
| 586 | Graph10Max = ResHisMax; |
| 587 | if (ComHisMax > Graph10Max) Graph10Max = ComHisMax; |
| 588 | if (IndHisMax > Graph10Max) Graph10Max = IndHisMax; |
| 589 | |
| 590 | Res2HisMax = 0; |
| 591 | Com2HisMax = 0; |
| 592 | Ind2HisMax = 0; |
| 593 | for (x = 238; x >= 120; x--) { |
| 594 | if (ResHis[x] > Res2HisMax) Res2HisMax = ResHis[x]; |
| 595 | if (ComHis[x] > Com2HisMax) Com2HisMax = ComHis[x]; |
| 596 | if (IndHis[x] > Ind2HisMax) Ind2HisMax = IndHis[x]; |
| 597 | if (ResHis[x] < 0) ResHis[x] = 0; |
| 598 | if (ComHis[x] < 0) ComHis[x] = 0; |
| 599 | if (IndHis[x] < 0) IndHis[x] = 0; |
| 600 | } |
| 601 | Graph120Max = Res2HisMax; |
| 602 | if (Com2HisMax > Graph120Max) Graph120Max = Com2HisMax; |
| 603 | if (Ind2HisMax > Graph120Max) Graph120Max = Ind2HisMax; |
| 604 | } |
| 605 | |
| 606 | |
| 607 | InitNewGraph(SimGraph *graph) |
| 608 | { |
| 609 | int d = 8; |
| 610 | struct XDisplay *xd; |
| 611 | |
| 612 | graph->next = NULL; |
| 613 | graph->range = 10; |
| 614 | graph->mask = ALL_HISTORIES; |
| 615 | |
| 616 | /* This stuff was initialized in our caller (GraphCmd) */ |
| 617 | /* graph->tkwin = NULL; */ |
| 618 | /* graph->interp = NULL; */ |
| 619 | /* graph->flags = 0; */ |
| 620 | |
| 621 | graph->x = NULL; |
| 622 | graph->visible = 0; |
| 623 | graph->w_x = graph->w_y = 0; |
| 624 | graph->w_width = graph->w_height = 0; |
| 625 | graph->pixmap = None; |
| 626 | graph->pixels = NULL; |
| 627 | graph->fontPtr = NULL; |
| 628 | graph->border = NULL; |
| 629 | graph->borderWidth = 0; |
| 630 | graph->relief = TK_RELIEF_FLAT; |
| 631 | graph->draw_graph_token = 0; |
| 632 | //fprintf(stderr, "InitNewGraph token %d\n", graph->draw_graph_token); |
| 633 | |
| 634 | graph->x = FindXDisplay(graph->tkwin); |
| 635 | IncRefDisplay(graph->x); |
| 636 | |
| 637 | graph->pixels = graph->x->pixels; |
| 638 | graph->fontPtr = NULL; |
| 639 | |
| 640 | DoResizeGraph(graph, 16, 16); |
| 641 | } |
| 642 | |
| 643 | |
| 644 | DestroyGraph(SimGraph *graph) |
| 645 | { |
| 646 | SimGraph **gp; |
| 647 | |
| 648 | for (gp = &sim->graph; |
| 649 | (*gp) != NULL; |
| 650 | gp = &((*gp)->next)) { |
| 651 | if ((*gp) == graph) { |
| 652 | (*gp) = graph->next; |
| 653 | sim->graphs--; |
| 654 | break; |
| 655 | } |
| 656 | } |
| 657 | |
| 658 | if (graph->pixmap != None) { |
| 659 | XFreePixmap(graph->x->dpy, graph->pixmap); |
| 660 | graph->pixmap = None; |
| 661 | } |
| 662 | |
| 663 | DecRefDisplay(graph->x); |
| 664 | |
| 665 | ckfree((char *) graph); |
| 666 | } |
| 667 | |
| 668 | |
| 669 | DoResizeGraph(SimGraph *graph, int w, int h) |
| 670 | { |
| 671 | int resize = 0; |
| 672 | |
| 673 | graph->w_width = w; graph->w_height = h; |
| 674 | |
| 675 | if (graph->pixmap != None) { |
| 676 | XFreePixmap(graph->x->dpy, graph->pixmap); |
| 677 | graph->pixmap = None; |
| 678 | } |
| 679 | graph->pixmap = XCreatePixmap(graph->x->dpy, graph->x->root, |
| 680 | w, h, graph->x->depth); |
| 681 | if (graph->pixmap == None) { |
| 682 | fprintf(stderr, |
| 683 | "Sorry, Micropolis can't create a pixmap on X display \"%s\".\n", |
| 684 | graph->x->display); |
| 685 | sim_exit(1); // Just sets tkMustExit and ExitReturn |
| 686 | return; |
| 687 | } |
| 688 | } |
| 689 | |
| 690 | |
| 691 | DoNewGraph(SimGraph *graph) |
| 692 | { |
| 693 | sim->graphs++; graph->next = sim->graph; sim->graph = graph; |
| 694 | |
| 695 | NewGraph = 1; |
| 696 | } |
| 697 | |
| 698 | |
| 699 | #define BORDER 5 |
| 700 | |
| 701 | DoUpdateGraph(SimGraph *graph) |
| 702 | { |
| 703 | Display *dpy; |
| 704 | GC gc; |
| 705 | Pixmap pm; |
| 706 | int *pix; |
| 707 | unsigned char **hist; |
| 708 | int w, h, mask, i, j, x, y; |
| 709 | XPoint points[121]; |
| 710 | int year = (CityTime / 48) + StartingYear; |
| 711 | int month = (CityTime / 4) % 12; |
| 712 | int do_top_labels = 0; |
| 713 | int do_right_labels = 0; |
| 714 | int top_label_height = 30; |
| 715 | int right_label_width = 65; |
| 716 | int tx, ty; |
| 717 | float sx, sy; |
| 718 | |
| 719 | if (!graph->visible) { |
| 720 | return; |
| 721 | } |
| 722 | |
| 723 | if (graph->range == 10) { |
| 724 | hist = History10; |
| 725 | } else { |
| 726 | hist = History120; |
| 727 | } |
| 728 | |
| 729 | dpy = graph->x->dpy; |
| 730 | gc = graph->x->gc; |
| 731 | pm = graph->pixmap; |
| 732 | pix = graph->pixels; |
| 733 | |
| 734 | w = graph->w_width; |
| 735 | h = graph->w_height; |
| 736 | |
| 737 | XSetFont(graph->x->dpy, graph->x->gc, graph->fontPtr->fid); |
| 738 | XSetLineAttributes(dpy, gc, 3, LineSolid, CapButt, JoinBevel); |
| 739 | if (graph->x->color) { |
| 740 | XSetForeground(dpy, gc, pix[COLOR_LIGHTGRAY]); |
| 741 | } else { |
| 742 | XSetForeground(dpy, gc, pix[COLOR_WHITE]); |
| 743 | } |
| 744 | XFillRectangle(dpy, pm, gc, 0, 0, w, h); |
| 745 | |
| 746 | tx = BORDER; ty = BORDER; |
| 747 | |
| 748 | if ((w -= (2 * BORDER)) < 1) w = 1; |
| 749 | if ((h -= (2 * BORDER)) < 1) h = 1; |
| 750 | |
| 751 | if (w > (4 * right_label_width)) { |
| 752 | w -= right_label_width; |
| 753 | do_right_labels = 1; |
| 754 | } |
| 755 | |
| 756 | if (do_right_labels && |
| 757 | (h > (3 * top_label_height))) { |
| 758 | ty += top_label_height; |
| 759 | h -= top_label_height; |
| 760 | do_top_labels = 1; |
| 761 | } |
| 762 | |
| 763 | sx = ((float)w) / 120.0; sy = ((float)h) / 256.0; |
| 764 | |
| 765 | mask = graph->mask; |
| 766 | for (i = 0; i < HISTORIES; i++, mask >>= 1, hist++) { |
| 767 | if (mask & 1) { |
| 768 | int fg = COLOR_WHITE; |
| 769 | int bg = COLOR_BLACK; |
| 770 | Pixmap stipple = None; |
| 771 | |
| 772 | for (j = 0; j < 120; j++) { |
| 773 | x = tx + (j * sx); |
| 774 | y = ty + ((int)(h - (((float)(*hist)[j]) * sy))); |
| 775 | points[j].x = x; points[j].y = y; |
| 776 | } |
| 777 | x = tx + (j * sx); |
| 778 | points[j].x = x; points[j].y = y; |
| 779 | |
| 780 | if (graph->x->color) { |
| 781 | XSetForeground(dpy, gc, pix[HistColor[i]]); |
| 782 | } else { |
| 783 | switch (i) { |
| 784 | case 0: /* res */ |
| 785 | stipple = graph->x->gray50_stipple; |
| 786 | break; |
| 787 | case 1: /* com */ |
| 788 | stipple = graph->x->gray25_stipple; |
| 789 | break; |
| 790 | case 2: /* ind */ |
| 791 | stipple = graph->x->gray75_stipple; |
| 792 | break; |
| 793 | case 3: /* cash */ |
| 794 | fg = COLOR_BLACK; |
| 795 | break; |
| 796 | case 4: /* crime */ |
| 797 | stipple = graph->x->horiz_stipple; |
| 798 | break; |
| 799 | case 5: /* pol */ |
| 800 | stipple = graph->x->vert_stipple; |
| 801 | break; |
| 802 | } |
| 803 | if (stipple != None) { |
| 804 | XSetStipple(graph->x->dpy, gc, stipple); |
| 805 | XSetTSOrigin(graph->x->dpy, gc, 0, 0); |
| 806 | XSetForeground(graph->x->dpy, gc, pix[fg]); |
| 807 | XSetBackground(graph->x->dpy, gc, pix[bg]); |
| 808 | XSetFillStyle(graph->x->dpy, gc, FillOpaqueStippled); |
| 809 | } else { |
| 810 | XSetForeground(graph->x->dpy, gc, pix[fg]); |
| 811 | } |
| 812 | } |
| 813 | |
| 814 | XDrawLines(dpy, pm, gc, points, 121, CoordModeOrigin); |
| 815 | |
| 816 | if (!graph->x->color && (stipple != None)) { |
| 817 | XSetFillStyle(graph->x->dpy, gc, FillSolid); |
| 818 | } |
| 819 | |
| 820 | if (do_right_labels) { |
| 821 | if (graph->x->color) { |
| 822 | XSetForeground(dpy, gc, pix[HistColor[i]]); |
| 823 | XDrawString(graph->x->dpy, pm, graph->x->gc, |
| 824 | x + 4, y + 5, |
| 825 | HistName[i], strlen(HistName[i])); |
| 826 | XDrawString(graph->x->dpy, pm, graph->x->gc, |
| 827 | x + 5, y + 4, |
| 828 | HistName[i], strlen(HistName[i])); |
| 829 | |
| 830 | XSetForeground(dpy, gc, pix[COLOR_BLACK]); |
| 831 | XDrawString(graph->x->dpy, pm, graph->x->gc, |
| 832 | x + 5, y + 5, |
| 833 | HistName[i], strlen(HistName[i])); |
| 834 | } else { |
| 835 | XSetForeground(dpy, gc, pix[COLOR_BLACK]); |
| 836 | XDrawString(graph->x->dpy, pm, graph->x->gc, |
| 837 | x + 5, y + 5, |
| 838 | HistName[i], strlen(HistName[i])); |
| 839 | } |
| 840 | } |
| 841 | } |
| 842 | } |
| 843 | |
| 844 | XSetLineAttributes(dpy, gc, 1, LineSolid, CapButt, JoinMiter); |
| 845 | |
| 846 | XSetForeground(dpy, gc, pix[COLOR_BLACK]); |
| 847 | XDrawLine(dpy, pm, gc, tx, ty - 1, tx + w, ty - 1); |
| 848 | XDrawLine(dpy, pm, gc, tx, ty + h, tx + w, ty + h); |
| 849 | |
| 850 | if (graph->range == 10) { |
| 851 | for (x = 120 - month; x >= 0; x -= 12) { |
| 852 | int xx, yy; |
| 853 | xx = tx + (x * sx); |
| 854 | XDrawLine(dpy, pm, gc, xx, ty - 1, xx, ty + h); |
| 855 | if (do_top_labels) { |
| 856 | char buf[256]; |
| 857 | |
| 858 | sprintf(buf, "%d", year--); |
| 859 | xx = tx + (x * sx) + 2; |
| 860 | yy = ty - ((year & 1) ? 4 : 20); |
| 861 | XDrawString(graph->x->dpy, pm, graph->x->gc, |
| 862 | xx, yy, buf, strlen(buf)); |
| 863 | } |
| 864 | } |
| 865 | } else { |
| 866 | int past; |
| 867 | |
| 868 | sx /= 10; |
| 869 | past = 10 * (year % 10); |
| 870 | year /= 10; |
| 871 | |
| 872 | for (x = 1200 - past; x >= 0; x -= 120) { |
| 873 | int xx, yy; |
| 874 | xx = tx + (x * sx); |
| 875 | XDrawLine(dpy, pm, gc, xx, ty - 1, xx, ty + h); |
| 876 | if (do_top_labels) { |
| 877 | char buf[256]; |
| 878 | |
| 879 | sprintf(buf, "%d0", year--); |
| 880 | |
| 881 | xx = tx + (x * sx) + 2; |
| 882 | yy = ty - ((year & 1) ? 4 : 20); |
| 883 | XDrawString(graph->x->dpy, pm, graph->x->gc, |
| 884 | xx, yy, buf, strlen(buf)); |
| 885 | } |
| 886 | } |
| 887 | } |
| 888 | |
| 889 | XCopyArea(graph->x->dpy, graph->pixmap, |
| 890 | Tk_WindowId(graph->tkwin), graph->x->gc, |
| 891 | 0, 0, graph->w_width, graph->w_height, 0, 0); |
| 892 | } |
| 893 | |
| 894 | |