267 | | |
268 | | # if (0) |
269 | | sx0 = x[i]*mxi + y[i]*mxj + bx; |
270 | | sy0 = x[i]*myi + y[i]*myj + by; |
271 | | sx0 = MIN (MAX (sx0, X0), X1); |
272 | | sy0 = MAX (MIN (sy0, Y0), Y1); |
273 | | |
274 | | /* continue with rest of points */ |
275 | | for (i++; i < object[0].Npts; i++) { |
| 287 | } |
| 288 | |
| 289 | // uses object->color |
| 290 | # define HISTOGRAM_SOLID(X_VALUE, Y_VALUE, DX_VAL) { \ |
| 291 | /* histogram bar corners */ \ |
| 292 | double sxmin = (X_VALUE) - 0.5*(DX_VAL); \ |
| 293 | double sxmax = (X_VALUE) + 0.5*(DX_VAL); \ |
| 294 | double symin = Xaxis; \ |
| 295 | double symax = (Y_VALUE); \ |
| 296 | /* saturated values for corner coords: */ \ |
| 297 | sxmin = MIN (MAX (sxmin, X0), X1); \ |
| 298 | sxmax = MIN (MAX (sxmax, X0), X1); \ |
| 299 | symin = MAX (MIN (symin, Y0), Y1); \ |
| 300 | symax = MAX (MIN (symax, Y0), Y1); \ |
| 301 | double dy = fabs(symax - symin); \ |
| 302 | double ylow = MIN(symin, symax); \ |
| 303 | FillRectangle (sxmin, ylow, (DX_VAL), dy); } |
| 304 | |
| 305 | // uses object->color |
| 306 | # define HISTOGRAM_OUTLINE(X_VALUE, Y_VALUE, DX_VAL) { \ |
| 307 | /* histogram bar corners */ \ |
| 308 | double sxmin = (X_VALUE) - 0.5*(DX_VAL); \ |
| 309 | double sxmax = (X_VALUE) + 0.5*(DX_VAL); \ |
| 310 | double symin = Xaxis; \ |
| 311 | double symax = (Y_VALUE); \ |
| 312 | /* saturated values for corner coords: */ \ |
| 313 | sxmin = MIN (MAX (sxmin, X0), X1); \ |
| 314 | sxmax = MIN (MAX (sxmax, X0), X1); \ |
| 315 | symin = MAX (MIN (symin, Y0), Y1); \ |
| 316 | symax = MAX (MIN (symax, Y0), Y1); \ |
| 317 | double dy = fabs(symax - symin); \ |
| 318 | double ylow = MAX(symin, symax); \ |
| 319 | DrawRectangle (sxmin, ylow, (DX_VAL), dy); } |
| 320 | |
| 321 | # define HISTOGRAM_OUTFILL(X_VALUE, Y_VALUE, DX_VAL) { \ |
| 322 | /* histogram bar corners */ \ |
| 323 | double sxmin = (X_VALUE) - 0.5*(DX_VAL); \ |
| 324 | double sxmax = (X_VALUE) + 0.5*(DX_VAL); \ |
| 325 | double symin = Xaxis; \ |
| 326 | double symax = (Y_VALUE); \ |
| 327 | /* saturated values for corner coords: */ \ |
| 328 | sxmin = MIN (MAX (sxmin, X0), X1); \ |
| 329 | sxmax = MIN (MAX (sxmax, X0), X1); \ |
| 330 | symin = MIN (MAX (symin, Y0), Y1); \ |
| 331 | symax = MIN (MAX (symax, Y0), Y1); \ |
| 332 | double dy = fabs(symax - symin); \ |
| 333 | double ylow = MIN(symin, symax); \ |
| 334 | FillRectangle (sxmin, ylow, (DX_VAL), dy); \ |
| 335 | fprintf (f, "0.00 0.00 0.00 setrgbcolor\n"); \ |
| 336 | DrawRectangle (sxmin, ylow, (DX_VAL), dy); \ |
| 337 | fprintf (f, "%s setrgbcolor\n", KapaColorRGBString(object->color)); } |
| 338 | |
| 339 | void PSBars (KapaGraphWidget *graph, Gobjects *object, FILE *f, int mode) { |
| 340 | |
| 341 | double mxi = graph[0].axis[0].dfx / (object[0].x1 - object[0].x0); // slope of the x-axis in x-pixels |
| 342 | double mxj = graph[0].axis[1].dfx / (object[0].y1 - object[0].y0); // slope of the x-axis in y-pixels (always 0 for now) |
| 343 | double myi = graph[0].axis[0].dfy / (object[0].x1 - object[0].x0); // slope of the x-axis in x-pixels (always 0 for now) |
| 344 | double myj = graph[0].axis[1].dfy / (object[0].y1 - object[0].y0); // slope of the x-axis in x-pixels |
| 345 | |
| 346 | // intercepts of axes |
| 347 | double bxi = graph[0].axis[0].fx - object[0].x0*graph[0].axis[0].dfx/(object[0].x1 - object[0].x0); |
| 348 | double bxj = -object[0].y0*graph[0].axis[1].dfx/(object[0].y1 - object[0].y0); |
| 349 | double byi = -object[0].x0*graph[0].axis[0].dfy/(object[0].x1 - object[0].x0); |
| 350 | double byj = graph[0].axis[1].fy - object[0].y0*graph[0].axis[1].dfy/(object[0].y1 - object[0].y0); |
| 351 | |
| 352 | double bx = bxi + bxj; |
| 353 | double by = byi + byj; |
| 354 | |
| 355 | // corner coords |
| 356 | double X0 = graph[0].axis[0].fx; |
| 357 | double X1 = graph[0].axis[0].fx + graph[0].axis[0].dfx; |
| 358 | double Y0 = graph[0].axis[1].fy; |
| 359 | double Y1 = graph[0].axis[1].fy + graph[0].axis[1].dfy; |
| 360 | // NOTE: Y0 > Y1 (dfy is negative) |
| 361 | |
| 362 | /* find the first valid datapoint */ |
| 363 | float *x = object[0].x; |
| 364 | float *y = object[0].y; |
| 365 | |
| 366 | /* we are drawing bars which are filled rectangles of height y[i] and width 0.5*dx |
| 367 | one point at a time |
| 368 | |
| 369 | I need to know the distance to the next and prev points to calculate dx |
| 370 | for the first point, dx is x[1] - x[0] |
| 371 | for the last point, dx is x[-1] - x[-2] (x[-1] is the last point) |
| 372 | for the rest, dx is 0.5*(x[i+1] - x[i-1]) |
| 373 | |
| 374 | rather than working out complex on-the-fly logic, I want to find the first and last |
| 375 | valid point in an initial pass, then calculate the above for the remainder. |
| 376 | TBD: make an index vector of only valid points? |
| 377 | */ |
| 378 | |
| 379 | int Ngood = 0; |
| 380 | ALLOCATE_PTR (goodPoint, int, object[0].Npts); |
| 381 | |
| 382 | // x = ??, y = 0: this assumes the xaxis is parallel to the plot window (myi = 0) |
| 383 | // note: Y0 > Y1 : y runs from large on bottom to small on top |
| 384 | float Xaxis = by; |
| 385 | Xaxis = MAX (Y1, MIN (Xaxis, Y0)); |
| 386 | |
| 387 | for (int i = 0; i < object[0].Npts; i++) { |
277 | | sx1 = x[i]*mxi + y[i]*mxj + bx; |
278 | | sy1 = x[i]*myi + y[i]*myj + by; |
279 | | sx1 = MIN (MAX (sx1, X0), X1); |
280 | | sy1 = MAX (MIN (sy1, Y0), Y1); |
281 | | sxa = 0.5*(sx0 + sx1); |
282 | | DrawLine (sx0, sy0, sxa, sy0); |
283 | | DrawLine (sxa, sy0, sxa, sy1); |
284 | | DrawLine (sxa, sy1, sx1, sy1); |
285 | | sx0 = sx1; sy0 = sy1; |
286 | | } |
287 | | # endif |
| 389 | |
| 390 | // coordinate on the screen of the point: |
| 391 | double sx1r = x[i]*mxi + y[i]*mxj + bx; |
| 392 | |
| 393 | if (sx1r < X0) { |
| 394 | // point to the left, skip it |
| 395 | continue; |
| 396 | } |
| 397 | if (sx1r > X1) { |
| 398 | // point to the right, skip it |
| 399 | continue; |
| 400 | } |
| 401 | |
| 402 | goodPoint[Ngood] = i; |
| 403 | Ngood ++; |
| 404 | } |
| 405 | |
| 406 | if (Ngood == 0) { |
| 407 | free (goodPoint); |
| 408 | return; |
| 409 | } |
| 410 | |
| 411 | // if we only have 1 point, draw bar half the width of the screen |
| 412 | // this works fine, but the auto limits for 1 value are somewhat silly |
| 413 | if (Ngood == 1) { |
| 414 | // coordinate on the screen of the point: |
| 415 | int n = goodPoint[0]; |
| 416 | double sx1r = x[n]*mxi + y[n]*mxj + bx; |
| 417 | double sy1r = x[n]*myi + y[n]*myj + by; |
| 418 | |
| 419 | float dx = 0.5*object->size*(X1 - X0); |
| 420 | |
| 421 | switch (mode) { |
| 422 | case KAPA_PLOT_BARS_SOLID: |
| 423 | HISTOGRAM_SOLID(sx1r, sy1r, dx); |
| 424 | break; |
| 425 | case KAPA_PLOT_BARS_OUTLINE: |
| 426 | HISTOGRAM_OUTLINE(sx1r, sy1r, dx); |
| 427 | break; |
| 428 | case KAPA_PLOT_BARS_OUTFILL: |
| 429 | HISTOGRAM_OUTFILL(sx1r, sy1r, dx); |
| 430 | break; |
| 431 | default: |
| 432 | HISTOGRAM_SOLID(sx1r, sy1r, dx); |
| 433 | break; |
| 434 | } |
| 435 | |
| 436 | free (goodPoint); |
| 437 | return; |
| 438 | } |
| 439 | |
| 440 | // first point: |
| 441 | { |
| 442 | int n; |
| 443 | n = goodPoint[1]; |
| 444 | double sx1r = x[n]*mxi + y[n]*mxj + bx; |
| 445 | |
| 446 | n = goodPoint[0]; |
| 447 | double sx0r = x[n]*mxi + y[n]*mxj + bx; |
| 448 | double sy0r = x[n]*myi + y[n]*myj + by; |
| 449 | |
| 450 | double dx = 0.5*object->size*(sx1r - sx0r); |
| 451 | |
| 452 | switch (mode) { |
| 453 | case KAPA_PLOT_BARS_SOLID: |
| 454 | HISTOGRAM_SOLID(sx0r, sy0r, dx); |
| 455 | break; |
| 456 | case KAPA_PLOT_BARS_OUTLINE: |
| 457 | HISTOGRAM_OUTLINE(sx0r, sy0r, dx); |
| 458 | break; |
| 459 | case KAPA_PLOT_BARS_OUTFILL: |
| 460 | HISTOGRAM_OUTFILL(sx0r, sy0r, dx); |
| 461 | break; |
| 462 | default: |
| 463 | HISTOGRAM_SOLID(sx0r, sy0r, dx); |
| 464 | break; |
| 465 | } |
| 466 | } |
| 467 | |
| 468 | for (int i = 1; i < Ngood - 1; i++) { |
| 469 | |
| 470 | int n; |
| 471 | n = goodPoint[i + 1]; |
| 472 | double sx2r = x[n]*mxi + y[n]*mxj + bx; |
| 473 | |
| 474 | n = goodPoint[i - 1]; |
| 475 | double sx0r = x[n]*mxi + y[n]*mxj + bx; |
| 476 | |
| 477 | // below we have 2 factors of 0.5 (we want half of the average spacing) |
| 478 | double dx = 0.5*0.5*object->size*(sx2r - sx0r); |
| 479 | |
| 480 | n = goodPoint[i]; |
| 481 | double sx1r = x[n]*mxi + y[n]*mxj + bx; |
| 482 | double sy1r = x[n]*myi + y[n]*myj + by; |
| 483 | |
| 484 | switch (mode) { |
| 485 | case KAPA_PLOT_BARS_SOLID: |
| 486 | HISTOGRAM_SOLID(sx1r, sy1r, dx); |
| 487 | break; |
| 488 | case KAPA_PLOT_BARS_OUTLINE: |
| 489 | HISTOGRAM_OUTLINE(sx1r, sy1r, dx); |
| 490 | break; |
| 491 | case KAPA_PLOT_BARS_OUTFILL: |
| 492 | HISTOGRAM_OUTFILL(sx1r, sy1r, dx); |
| 493 | break; |
| 494 | default: |
| 495 | HISTOGRAM_SOLID(sx1r, sy1r, dx); |
| 496 | break; |
| 497 | } |
| 498 | } |
| 499 | |
| 500 | // last point: |
| 501 | { |
| 502 | int n; |
| 503 | n = goodPoint[Ngood - 1]; |
| 504 | double sx1r = x[n]*mxi + y[n]*mxj + bx; |
| 505 | double sy1r = x[n]*myi + y[n]*myj + by; |
| 506 | |
| 507 | n = goodPoint[Ngood - 2]; |
| 508 | double sx0r = x[n]*mxi + y[n]*mxj + bx; |
| 509 | |
| 510 | double dx = 0.5*object->size*(sx1r - sx0r); |
| 511 | |
| 512 | switch (mode) { |
| 513 | case KAPA_PLOT_BARS_SOLID: |
| 514 | HISTOGRAM_SOLID(sx1r, sy1r, dx); |
| 515 | break; |
| 516 | case KAPA_PLOT_BARS_OUTLINE: |
| 517 | HISTOGRAM_OUTLINE(sx1r, sy1r, dx); |
| 518 | break; |
| 519 | case KAPA_PLOT_BARS_OUTFILL: |
| 520 | HISTOGRAM_OUTFILL(sx1r, sy1r, dx); |
| 521 | break; |
| 522 | default: |
| 523 | HISTOGRAM_SOLID(sx1r, sy1r, dx); |
| 524 | break; |
| 525 | } |
| 526 | } |
| 527 | free (goodPoint); |
| 528 | return; |