123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523 |
- /* This file is part of the GNU plotutils package. Copyright (C) 1995,
- 1996, 1997, 1998, 1999, 2000, 2005, 2008, Free Software Foundation, Inc.
- The GNU plotutils package is free software. You may redistribute it
- and/or modify it under the terms of the GNU General Public License as
- published by the Free Software foundation; either version 2, or (at your
- option) any later version.
- The GNU plotutils package is distributed in the hope that it will be
- useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- General Public License for more details.
- You should have received a copy of the GNU General Public License along
- with the GNU plotutils package; see the file COPYING. If not, write to
- the Free Software Foundation, Inc., 51 Franklin St., Fifth Floor,
- Boston, MA 02110-1301, USA. */
- // The common_output class: subclassed from the output class,
- // providing support for dotted/dashed arcs and circles, and for
- // rounded boxes (possible dotted/dashed).
- #include "pic.h"
- #include "output.h"
- #include "common.h"
- // output a dashed circle as a series of arcs
- void
- common_output::dashed_circle(const position ¢, double rad, const line_type <)
- {
- assert (lt.type == line_type::dashed);
- line_type slt = lt;
- slt.type = line_type::solid;
- double dash_angle = lt.dash_width/rad; // dash angle in radians
- int ndashes;
- double gap_angle;
- if (dash_angle >= M_PI/4.0)
- {
- if (dash_angle < M_PI/2.0)
- {
- gap_angle = M_PI/2.0 - dash_angle;
- ndashes = 4;
- }
- else if (dash_angle < M_PI)
- {
- gap_angle = M_PI - dash_angle;
- ndashes = 2;
- }
- else
- {
- circle (cent, rad, slt, -1.0);
- return;
- }
- }
- else
- {
- ndashes = 4 * int(ceil(M_PI/(4.0*dash_angle)));
- gap_angle = (M_PI*2.0)/ndashes - dash_angle;
- }
- for (int i = 0; i < ndashes; i++)
- {
- double start_angle = i*(dash_angle+gap_angle) - 0.5 * dash_angle;
- solid_arc (cent, rad, start_angle, start_angle + dash_angle, lt);
- }
- }
- // output a dotted circle as a series of dots
- void
- common_output::dotted_circle(const position ¢, double rad, const line_type <)
- {
- double gap_angle = lt.dash_width/rad; // gap angle in radians
- int ndots;
- double ang = 0.0;
- assert (lt.type == line_type::dotted);
- if (gap_angle >= M_PI/2.0)
- {
- // always have at least 2 dots
- gap_angle = M_PI;
- ndots = 2;
- }
- else
- {
- ndots = 4*int(M_PI/(2.0*gap_angle));
- gap_angle = (M_PI*2.0)/ndots;
- }
- for (int i = 0; i < ndots; i++, ang += gap_angle)
- dot (cent + position(cos(ang), sin(ang))*rad, lt);
- }
- // return non-zero iff we can compute a center
- int
- compute_arc_center(const position &start, const position ¢, const position &end, position *result)
- {
- // This finds the point along the vector from start to cent that
- // is equidistant between start and end.
- distance c = cent - start;
- distance e = end - start;
- double n = c*e;
- if (n == 0.0)
- return 0;
- else
- {
- *result = start + c*((e*e)/(2.0*n));
- return 1;
- }
- }
- // output a dashed arc as a series of arcs
- void
- common_output::dashed_arc(const position &start, const position ¢, const position &end, const line_type <)
- {
- assert (lt.type == line_type::dashed);
- position c;
- if (!compute_arc_center(start, cent, end, &c))
- {
- line(start, &end, 1, lt);
- return;
- }
- distance start_offset = start - c;
- distance end_offset = end - c;
- double start_angle = atan2(start_offset.y, start_offset.x);
- double end_angle = atan2(end_offset.y, end_offset.x);
- double rad = hypot(c - start);
- double dash_angle = lt.dash_width/rad;
- double total_angle = end_angle - start_angle;
- while (total_angle < 0)
- total_angle += M_PI + M_PI;
- if (total_angle <= dash_angle*2.0)
- {
- solid_arc(cent, rad, start_angle, end_angle, lt);
- return;
- }
- int ndashes = int((total_angle - dash_angle)/(dash_angle*2.0) + 0.5);
- double dash_and_gap_angle = (total_angle - dash_angle)/ndashes;
- for (int i = 0; i <= ndashes; i++)
- solid_arc(cent, rad, start_angle + i*dash_and_gap_angle,
- start_angle + i*dash_and_gap_angle + dash_angle, lt);
- }
- // output a dotted arc as a series of dots
- void
- common_output::dotted_arc(const position &start, const position ¢, const position &end, const line_type <)
- {
- assert (lt.type == line_type::dotted);
- position c;
- if (!compute_arc_center(start, cent, end, &c))
- {
- line(start, &end, 1, lt);
- return;
- }
- distance start_offset = start - c;
- distance end_offset = end - c;
- double start_angle = atan2(start_offset.y, start_offset.x);
- double total_angle = atan2(end_offset.y, end_offset.x) - start_angle;
- while (total_angle < 0)
- total_angle += M_PI + M_PI;
- double rad = hypot(c - start);
- int ndots = int(total_angle/(lt.dash_width/rad) + .5);
- if (ndots == 0)
- dot (start, lt);
- else
- {
- for (int i = 0; i <= ndots; i++)
- {
- double a = start_angle + (total_angle*i)/ndots;
- dot (cent + position(cos(a), sin(a))*rad, lt);
- }
- }
- }
- // output a solid arc, of the sort used in dashing
- void
- common_output::solid_arc(const position ¢, double rad, double start_angle, double end_angle, const line_type <)
- {
- line_type slt = lt;
- slt.type = line_type::solid;
- arc (cent + position(cos(start_angle), sin(start_angle))*rad,
- cent,
- cent + position(cos(end_angle), sin(end_angle))*rad,
- slt);
- }
- // output a rounded box (of one of several line types)
- void
- common_output::rounded_box(const position ¢, const distance &dim, double rad, const line_type <, double fill)
- {
- if (fill >= 0.0)
- filled_rounded_box(cent, dim, rad, fill);
- switch (lt.type)
- {
- case line_type::invisible:
- break;
- case line_type::dashed:
- dashed_rounded_box(cent, dim, rad, lt);
- break;
- case line_type::dotted:
- dotted_rounded_box(cent, dim, rad, lt);
- break;
- case line_type::solid:
- solid_rounded_box(cent, dim, rad, lt);
- break;
- default:
- assert (0);
- }
- }
- // output a dashed rounded box as a series of arcs
- void
- common_output::dashed_rounded_box(const position ¢, const distance &dim, double rad, const line_type <)
- {
- line_type slt = lt;
- slt.type = line_type::solid;
- double hor_length = dim.x + (M_PI/2.0 - 2.0)*rad;
- int n_hor_dashes = int(hor_length/(lt.dash_width*2.0) + 0.5);
- double hor_gap_width = (n_hor_dashes != 0
- ? hor_length/n_hor_dashes - lt.dash_width
- : 0.0);
- double vert_length = dim.y + (M_PI/2.0 - 2.0)*rad;
- int n_vert_dashes = int(vert_length/(lt.dash_width*2.0) + 0.5);
- double vert_gap_width = (n_vert_dashes != 0
- ? vert_length/n_vert_dashes - lt.dash_width
- : 0.0);
- // Each corner arc must be split into two for dashing,
- // with one part dashed using vert_gap_width, and the other
- // using hor_gap_width.
- double offset = lt.dash_width/2.0;
- dash_arc(cent + position(dim.x/2.0 - rad, -dim.y/2.0 + rad), rad,
- -M_PI/4.0, 0, slt, lt.dash_width, vert_gap_width, &offset);
- dash_line(cent + position(dim.x/2.0, -dim.y/2.0 + rad),
- cent + position(dim.x/2.0, dim.y/2.0 - rad),
- slt, lt.dash_width, vert_gap_width, &offset);
- dash_arc(cent + position(dim.x/2.0 - rad, dim.y/2.0 - rad), rad,
- 0, M_PI/4.0, slt, lt.dash_width, vert_gap_width, &offset);
- offset = lt.dash_width/2.0;
- dash_arc(cent + position(dim.x/2.0 - rad, dim.y/2.0 - rad), rad,
- M_PI/4.0, M_PI/2, slt, lt.dash_width, hor_gap_width, &offset);
- dash_line(cent + position(dim.x/2.0 - rad, dim.y/2.0),
- cent + position(-dim.x/2.0 + rad, dim.y/2.0),
- slt, lt.dash_width, hor_gap_width, &offset);
- dash_arc(cent + position(-dim.x/2.0 + rad, dim.y/2.0 - rad), rad,
- M_PI/2, 3*M_PI/4.0, slt, lt.dash_width, hor_gap_width, &offset);
- offset = lt.dash_width/2.0;
- dash_arc(cent + position(-dim.x/2.0 + rad, dim.y/2.0 - rad), rad,
- 3.0*M_PI/4.0, M_PI, slt, lt.dash_width, vert_gap_width, &offset);
- dash_line(cent + position(-dim.x/2.0, dim.y/2.0 - rad),
- cent + position(-dim.x/2.0, -dim.y/2.0 + rad),
- slt, lt.dash_width, vert_gap_width, &offset);
- dash_arc(cent + position(-dim.x/2.0 + rad, -dim.y/2.0 + rad), rad,
- M_PI, 5.0*M_PI/4.0, slt, lt.dash_width, vert_gap_width, &offset);
- offset = lt.dash_width/2.0;
- dash_arc(cent + position(-dim.x/2.0 + rad, -dim.y/2.0 + rad), rad,
- 5*M_PI/4.0, 3*M_PI/2.0, slt, lt.dash_width, hor_gap_width, &offset);
- dash_line(cent + position(-dim.x/2.0 + rad, -dim.y/2.0),
- cent + position(dim.x/2.0 - rad, -dim.y/2.0),
- slt, lt.dash_width, hor_gap_width, &offset);
- dash_arc(cent + position(dim.x/2.0 - rad, -dim.y/2.0 + rad), rad,
- 3*M_PI/2, 7*M_PI/4, slt, lt.dash_width, hor_gap_width, &offset);
- }
- // Private, used by dashed_rounded_box.
- void
- common_output::dash_arc(const position ¢, double rad, double start_angle, double end_angle, const line_type <, double dash_width, double gap_width, double *offsetp)
- {
- double length = (end_angle - start_angle)*rad;
- double pos = 0.0;
- for (;;)
- {
- if (*offsetp >= dash_width)
- {
- double rem = dash_width + gap_width - *offsetp;
- if (pos + rem > length)
- {
- *offsetp += length - pos;
- break;
- }
- else
- {
- pos += rem;
- *offsetp = 0.0;
- }
- }
- else
- {
- double rem = dash_width - *offsetp;
- if (pos + rem > length)
- {
- solid_arc(cent, rad, start_angle + pos/rad, end_angle, lt);
- *offsetp += length - pos;
- break;
- }
- else
- {
- solid_arc(cent, rad, start_angle + pos/rad,
- start_angle + (pos + rem)/rad, lt);
- pos += rem;
- *offsetp = dash_width;
- }
- }
- }
- }
- // Private, used by dashed_rounded_box.
- void
- common_output::dash_line(const position &start, const position &end, const line_type <, double dash_width, double gap_width, double *offsetp)
- {
- distance dist = end - start;
- double length = hypot(dist);
- if (length == 0.0)
- return;
- double pos = 0.0;
- for (;;)
- {
- if (*offsetp >= dash_width)
- {
- double rem = dash_width + gap_width - *offsetp;
- if (pos + rem > length)
- {
- *offsetp += length - pos;
- break;
- }
- else
- {
- pos += rem;
- *offsetp = 0.0;
- }
- }
- else
- {
- double rem = dash_width - *offsetp;
- if (pos + rem > length)
- {
- line(start + dist*(pos/length), &end, 1, lt);
- *offsetp += length - pos;
- break;
- }
- else
- {
- position p(start + dist*((pos + rem)/length));
- line(start + dist*(pos/length), &p, 1, lt);
- pos += rem;
- *offsetp = dash_width;
- }
- }
- }
- }
- // output a dotted rounded box as a series of dots
- void
- common_output::dotted_rounded_box(const position ¢, const distance &dim, double rad, const line_type <)
- {
- line_type slt = lt;
- slt.type = line_type::solid;
- double hor_length = dim.x + (M_PI/2.0 - 2.0)*rad;
- int n_hor_dots = int(hor_length/lt.dash_width + .5);
- double hor_gap_width = (n_hor_dots != 0
- ? hor_length/n_hor_dots
- : lt.dash_width);
- double vert_length = dim.y + (M_PI/2.0 - 2.0)*rad;
- int n_vert_dots = int(vert_length/lt.dash_width + .5);
- double vert_gap_width = (n_vert_dots != 0
- ? vert_length/n_vert_dots
- : lt.dash_width);
- double epsilon = lt.dash_width/(rad*100.0);
- double offset = 0.0;
- dot_arc(cent + position(dim.x/2.0 - rad, -dim.y/2.0 + rad), rad,
- -M_PI/4.0, 0, slt, vert_gap_width, &offset);
- dot_line(cent + position(dim.x/2.0, -dim.y/2.0 + rad),
- cent + position(dim.x/2.0, dim.y/2.0 - rad),
- slt, vert_gap_width, &offset);
- dot_arc(cent + position(dim.x/2.0 - rad, dim.y/2.0 - rad), rad,
- 0, M_PI/4.0 - epsilon, slt, vert_gap_width, &offset);
- offset = 0.0;
- dot_arc(cent + position(dim.x/2.0 - rad, dim.y/2.0 - rad), rad,
- M_PI/4.0, M_PI/2, slt, hor_gap_width, &offset);
- dot_line(cent + position(dim.x/2.0 - rad, dim.y/2.0),
- cent + position(-dim.x/2.0 + rad, dim.y/2.0),
- slt, hor_gap_width, &offset);
- dot_arc(cent + position(-dim.x/2.0 + rad, dim.y/2.0 - rad), rad,
- M_PI/2, 3*M_PI/4.0 - epsilon, slt, hor_gap_width, &offset);
- offset = 0.0;
- dot_arc(cent + position(-dim.x/2.0 + rad, dim.y/2.0 - rad), rad,
- 3.0*M_PI/4.0, M_PI, slt, vert_gap_width, &offset);
- dot_line(cent + position(-dim.x/2.0, dim.y/2.0 - rad),
- cent + position(-dim.x/2.0, -dim.y/2.0 + rad),
- slt, vert_gap_width, &offset);
- dot_arc(cent + position(-dim.x/2.0 + rad, -dim.y/2.0 + rad), rad,
- M_PI, 5.0*M_PI/4.0 - epsilon, slt, vert_gap_width, &offset);
- offset = 0.0;
- dot_arc(cent + position(-dim.x/2.0 + rad, -dim.y/2.0 + rad), rad,
- 5*M_PI/4.0, 3*M_PI/2.0, slt, hor_gap_width, &offset);
- dot_line(cent + position(-dim.x/2.0 + rad, -dim.y/2.0),
- cent + position(dim.x/2.0 - rad, -dim.y/2.0),
- slt, hor_gap_width, &offset);
- dot_arc(cent + position(dim.x/2.0 - rad, -dim.y/2.0 + rad), rad,
- 3*M_PI/2, 7*M_PI/4 - epsilon, slt, hor_gap_width, &offset);
- }
- // Private, used by dotted_rounded_box.
- void
- common_output::dot_arc(const position ¢, double rad, double start_angle, double end_angle, const line_type <, double gap_width, double *offsetp)
- {
- double length = (end_angle - start_angle)*rad;
- double pos = 0.0;
- for (;;)
- {
- if (*offsetp == 0.0)
- {
- double ang = start_angle + pos/rad;
- dot (cent + position(cos(ang), sin(ang))*rad, lt);
- }
- double rem = gap_width - *offsetp;
- if (pos + rem > length)
- {
- *offsetp += length - pos;
- break;
- }
- else
- {
- pos += rem;
- *offsetp = 0.0;
- }
- }
- }
- // Private, used by dotted_rounded_box.
- void
- common_output::dot_line(const position &start, const position &end, const line_type <, double gap_width, double *offsetp)
- {
- distance dist = end - start;
- double length = hypot(dist);
- if (length == 0.0)
- return;
- double pos = 0.0;
- for (;;)
- {
- if (*offsetp == 0.0)
- dot (start + dist*(pos/length), lt);
- double rem = gap_width - *offsetp;
- if (pos + rem > length)
- {
- *offsetp += length - pos;
- break;
- }
- else
- {
- pos += rem;
- *offsetp = 0.0;
- }
- }
- }
- // draw a solid rounded box, from arcs and lines
- void
- common_output::solid_rounded_box(const position ¢, const distance &dim, double rad, const line_type <)
- {
- position tem = cent - dim/2.0;
- arc(tem + position(0.0, rad),
- tem + position(rad, rad),
- tem + position(rad, 0.0),
- lt);
- tem = cent + position(-dim.x/2.0, dim.y/2.0);
- arc(tem + position(rad, 0.0),
- tem + position(rad, -rad),
- tem + position(0.0, -rad),
- lt);
- tem = cent + dim/2.0;
- arc(tem + position(0.0, -rad),
- tem + position(-rad, -rad),
- tem + position(-rad, 0.0),
- lt);
- tem = cent + position(dim.x/2.0, -dim.y/2.0);
- arc(tem + position(-rad, 0.0),
- tem + position(-rad, rad),
- tem + position(0.0, rad),
- lt);
- position end;
- end = cent + position(-dim.x/2.0, dim.y/2.0 - rad);
- line(cent - dim/2.0 + position(0.0, rad), &end, 1, lt);
- end = cent + position(dim.x/2.0 - rad, dim.y/2.0);
- line(cent + position(-dim.x/2.0 + rad, dim.y/2.0), &end, 1, lt);
- end = cent + position(dim.x/2.0, -dim.y/2.0 + rad);
- line(cent + position(dim.x/2.0, dim.y/2.0 - rad), &end, 1, lt);
- end = cent + position(-dim.x/2.0 + rad, -dim.y/2.0);
- line(cent + position(dim.x/2.0 - rad, -dim.y/2.0), &end, 1, lt);
- }
- // Draw a filled rounded box, by drawing filled circles etc.
- // Not used by libplot driver.
- void
- common_output::filled_rounded_box(const position ¢, const distance &dim, double rad, double fill)
- {
- line_type ilt;
- ilt.type = line_type::invisible;
- circle(cent + position(dim.x/2.0 - rad, dim.y/2.0 - rad), rad, ilt, fill);
- circle(cent + position(-dim.x/2.0 + rad, dim.y/2.0 - rad), rad, ilt, fill);
- circle(cent + position(-dim.x/2.0 + rad, -dim.y/2.0 + rad), rad, ilt, fill);
- circle(cent + position(dim.x/2.0 - rad, -dim.y/2.0 + rad), rad, ilt, fill);
- position vec[4];
- vec[0] = cent + position(dim.x/2.0, dim.y/2.0 - rad);
- vec[1] = cent + position(-dim.x/2.0, dim.y/2.0 - rad);
- vec[2] = cent + position(-dim.x/2.0, -dim.y/2.0 + rad);
- vec[3] = cent + position(dim.x/2.0, -dim.y/2.0 + rad);
- polygon(vec, 4, ilt, fill);
- vec[0] = cent + position(dim.x/2.0 - rad, dim.y/2.0);
- vec[1] = cent + position(-dim.x/2.0 + rad, dim.y/2.0);
- vec[2] = cent + position(-dim.x/2.0 + rad, -dim.y/2.0);
- vec[3] = cent + position(dim.x/2.0 - rad, -dim.y/2.0);
- polygon(vec, 4, ilt, fill);
- }
|