Logo Search packages:      
Sourcecode: easychem version File versions  Download package

drawing.c

/*
 * EasyChem
 * A program for creating and editing molecular formulas.
 *
 * Copyright (C) 2003, 2004, 2005 Fran├žois-Xavier Coudert
 * 
 * Distributed under the General Public Licence (GPL).
 * See file COPYING in the source distribution for more details.
 *
 */

#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <gtk/gtk.h>
#include <pango/pango.h>

#include "common.h"
#include "auxi.h"
#include "drawing.h"


/*****************************************
 *                                       *
 *           O R N A M E N T S           *
 *                                       *
 *****************************************/

/* Draw only one ornaments, around point (x,y) and rotated with angle t */
void
draw_one_ornament (struct Ornament *orn, const LLINT xc, const LLINT yc,
                   double t, GdkGC *gc, GdkPixmap *pix, const double zoom)
{
  switch (orn->type)
  {
  case ORN_LONE_PAIR:
    gdk_draw_line (pix, gc,
      U2S(xc - ORN_DEF_SIZE * orn->size / 2 * sin (t)),
      U2S(yc - ORN_DEF_SIZE * orn->size / 2 * cos (t)),
      U2S(xc + ORN_DEF_SIZE * orn->size / 2 * sin (t)),
      U2S(yc + ORN_DEF_SIZE * orn->size / 2 * cos (t)));
    break;
    
  case ORN_GAP:
    gdk_draw_rectangle (pix, gc, FALSE,
      U2S(xc - (orn->size * ORN_DEF_SIZE) / 2),
      U2S(yc - (orn->size * ORN_DEF_SIZE) / 2),
      U2S(orn->size * ORN_DEF_SIZE),
      U2S(orn->size * ORN_DEF_SIZE));
    break;
    
  case ORN_GAP2:
    gdk_draw_rectangle (pix, gc, FALSE,
      U2S(xc - (orn->size * ORN_DEF_SIZE) / 2),
      U2S(yc - (orn->size * ORN_DEF_SIZE) / 4),
      U2S(orn->size * ORN_DEF_SIZE),
      U2S(orn->size * ORN_DEF_SIZE / 2));
    break;
    
  case ORN_RADICAL:
    gdk_draw_arc (pix, gc, TRUE,
      U2S(xc - (orn->size * ORN_DEF_SIZE * 0.6) / 2),
      U2S(yc - (orn->size * ORN_DEF_SIZE * 0.6) / 2),
      U2S(orn->size * ORN_DEF_SIZE * 0.6), 
      U2S(orn->size * ORN_DEF_SIZE * 0.6), 0, 23040);
    break;

  case ORN_LONE_PAIR_DOTS:
    gdk_draw_arc (pix, gc, TRUE,
      U2S(xc - ORN_DEF_SIZE * orn->size * 1 / 4 * sin(t)),
      U2S(yc - ORN_DEF_SIZE * orn->size * 1 / 4 * cos(t)),
      U2S(orn->size * ORN_DEF_SIZE * 0.1),
      U2S(orn->size * ORN_DEF_SIZE * 0.1), 0, 23040);
    gdk_draw_arc (pix, gc, TRUE,
      U2S(xc + ORN_DEF_SIZE * orn->size * 1 / 4 * sin(t)),
      U2S(yc + ORN_DEF_SIZE * orn->size * 1 / 4 * cos(t)),
      U2S(orn->size * ORN_DEF_SIZE * 0.1),
      U2S(orn->size * ORN_DEF_SIZE * 0.1), 0, 23040);
    break;

  case ORN_LONE_ELECTRON:
    gdk_draw_arc (pix, gc, TRUE,
      U2S(xc - ORN_DEF_SIZE * orn->size * 1 / 4 * sin(t)),
      U2S(yc - ORN_DEF_SIZE * orn->size * 1 / 4 * cos(t)),
      U2S(orn->size * ORN_DEF_SIZE * 0.1),
      U2S(orn->size * ORN_DEF_SIZE * 0.1), 0, 23040);
    break;
  } 
} 


/* To draw a whole list of ornaments */
void
draw_ornaments (struct Ornament *orn, const LLINT x, const LLINT y,
                const double zoom, GdkGC *gc, GdkPixmap *pix)
{
  struct Ornament *current = orn;
  unsigned int i;
  double tc, t_first, t, t_spacing;

  while (current != NULL)
  {
    tc = current->angle * G_PI / 180;
    t_spacing = current->spacing * G_PI / 180;
    t_first = tc - ((current->number - 1) * t_spacing) / 2;

    for (i = 0; i < current->number; i++)
    {
      t = t_first + i * t_spacing;
      draw_one_ornament (current, x + ORN_DEF_DIST * current->dist * sin (t),
                         y - ORN_DEF_DIST * current->dist * cos (t),
                         G_PI_2 - t, gc, pix, zoom);
    }

    current = current->next;
  }
}



/*****************************************
 *                                       *
 *     H A N D L I N G       T E X T     *
 *                                       *
 *****************************************/

#define FONT_SIZE       20000

void
pango_layout_set_rich_text (PangoLayout * p_layout, const gchar * text,
                            GdkColor * color, const int mode,
                            const double size, const double zoom)
{
  gchar *buff;

  if (mode == MODE_ORNAMENT)
  {
    buff =
      g_strdup_printf ("<span face=\"Serif\" size=\"%d\"><span foreground=\"#7F7F7F\">%s</span></span>",
                             (int) (FONT_SIZE * size * zoom), text);
  }
  else
  {
  if (color == NULL)
  buff =
    g_strdup_printf ("<span face=\"Serif\" size=\"%d\"><span foreground=\"#0000FF\">%s</span></span>",
                     (int) (FONT_SIZE * size * zoom), text);
  else
  {
    buff =
      g_strdup_printf ("<span face=\"Serif\" size=\"%d\"><span foreground=\"#%.2x%.2x%.2x\">%s</span></span>",
                     (int) (FONT_SIZE * size * zoom),
                     color->red / 256, color->green / 256, color->blue / 256,
                     text);
  }
  }

  pango_layout_set_markup (p_layout, buff, -1);
  g_free (buff);
}


void
adjust_atom (struct Bond *atom, GtkWidget * area, const int mode,
             const double zoom)
{
  PangoLayout *p_layout;
  PangoRectangle rect, rect2;
  int width, height, w2, h2;

  if (atom == NULL)
    return;

/* The x2 and y2 defined here are not the final ones, but the size of the
 * pango layout */
  p_layout = gtk_widget_create_pango_layout (area, "");
  pango_layout_set_rich_text (p_layout, atom->pango, NULL, mode, atom->width,
                              zoom);
  pango_layout_get_pixel_size (p_layout, &width, &height);
  width = S2U (width);
  height = S2U (height);
  g_object_unref (p_layout);

  p_layout = gtk_widget_create_pango_layout (area, "");
  switch (atom->type)
    {
    case BOND_ATOM:
      pango_layout_set_rich_text (p_layout, atom->pango, NULL,
                                mode, atom->width, zoom);
      pango_layout_get_extents (p_layout, &rect, NULL);
      atom->x1 = atom->x3 - width / 2;
      atom->y1 = atom->y3 - height / 2;
      atom->x4 = atom->x3 - S2U (rect.width / PANGO_SCALE) / 2;
      atom->y4 = atom->y3 - S2U (rect.height / PANGO_SCALE) / 2;
      atom->x2 = S2U (rect.width / PANGO_SCALE);
      atom->y2 = S2U (rect.height / PANGO_SCALE);
      break;

    case BOND_GROUP_L:
      pango_layout_set_rich_text (p_layout, atom->pango_left, NULL,
                                mode, atom->width, zoom);
      pango_layout_get_extents (p_layout, &rect, NULL);
      pango_layout_get_pixel_size (p_layout, &w2, &h2);
      atom->x1 = atom->x3 - S2U (w2) / 2;
      atom->y1 = atom->y3 - S2U (h2) / 2;
      atom->x4 = atom->x3 - S2U (rect.width / PANGO_SCALE) / 2;
      atom->y4 = atom->y3 - S2U (rect.height / PANGO_SCALE) / 2;
      pango_layout_set_rich_text (p_layout, atom->pango, NULL,
                                mode, atom->width, zoom);
      pango_layout_get_extents (p_layout, &rect, NULL);
      atom->x2 = S2U (rect.width / PANGO_SCALE);
      atom->y2 = S2U (rect.height / PANGO_SCALE);
      break;

    case BOND_GROUP_R:
      pango_layout_set_rich_text (p_layout, atom->pango_right, NULL,
                                mode, atom->width, zoom);
      pango_layout_get_extents (p_layout, &rect, NULL);
      pango_layout_get_pixel_size (p_layout, &w2, &h2);
      atom->x1 = atom->x3 - width + S2U (w2) / 2;
      atom->y1 = atom->y3 - height + S2U (h2) / 2;
      pango_layout_set_rich_text (p_layout, atom->pango, NULL, mode,
                                atom->width, zoom);
      pango_layout_get_extents (p_layout, &rect2, NULL);
      atom->x4 = atom->x3 - S2U (rect2.width / PANGO_SCALE)
        + S2U (rect.width / PANGO_SCALE) / 2;
      atom->y4 = atom->y3 - S2U (rect2.height / PANGO_SCALE)
        + S2U (rect2.height / PANGO_SCALE) / 2;
/*                 ^
 * This rect2.height should be a rect.height, but it doesn't work, and
 * the approximation is often quite good :) */
      atom->x2 = S2U (rect2.width / PANGO_SCALE);
      atom->y2 = S2U (rect2.height / PANGO_SCALE);
      break;
    }

  g_object_unref (p_layout);
}


void
adjust_all_atom_in_list (struct Bond *list, GtkWidget * area, const int mode,
                         const double zoom)
{
  struct Bond * current = list;

  while (current != NULL)
  {
    if (BOND_HAS_TEXT (current))
      adjust_atom (current, area, mode, zoom);
    if (current->type == BOND_GROUP)
      adjust_all_atom_in_list (current->group_members, area, mode, zoom);
    current = current->next;
  }
}



/*****************************************
 *                                       *
 *        D R A W     B O N D S          *
 *                                       *
 *****************************************/

#define DOUBLE_SHIFT    (0.04 * BOND_LENGTH * current->width)
#define TRIPLE_SHIFT    (0.06 * BOND_LENGTH * current->width)
#define STEREO_SHIFT    (0.08 * BOND_LENGTH * current->width)
#define ARROW_SHIFT     (0.12 * BOND_LENGTH * current->width)
#define ARROW2_SHIFT    (0.24 * BOND_LENGTH * current->width)

/* We draw the bonds */
void
draw_bond_list (struct Bond *list, const int recursive, const int selected,
                GtkWidget * area, GdkPixmap *pix, const int mode,
            const double zoom)
{
  PangoLayout *p_layout;
  GdkGC *gc;
  GdkPoint points[4];
  int i, j, dir;
  LLINT dx, dy;
  double x = 0, y = 0, r, cx, cy;
  struct Bond *current;

  if (!recursive)
    gdk_draw_rectangle (pix, area->style->white_gc, TRUE, 0, 0,
                  area->allocation.width, area->allocation.height);

  current = list;
  if (current != NULL)
    while (current != NULL)
      {
      if (mode == MODE_ORNAMENT)
        gc = gc_gray;
      else
      {
        if ((current->selected & SEL_YES) || (selected & SEL_YES))
          gc = gc_sel;
        else
          {
            gc = gdk_gc_new (area->window);
            gdk_gc_set_rgb_fg_color (gc, &(current->color));
          }
      }

      switch (current->type)
        {
        case BOND_SIMPLE:
          gdk_draw_line (pix, gc,
                     U2S (current->x1), U2S (current->y1),
                     U2S (current->x2), U2S (current->y2));
          break;

        case BOND_DOUBLE:
          r = hypot ((double) current->x2 - (double) current->x1,
                   (double) current->y2 - (double) current->y1);
          x = ((double) current->x2 - (double) current->x1) / r;
          y = ((double) current->y2 - (double) current->y1) / r;
          gdk_draw_line (pix, gc,
                     U2S (current->x1 - DOUBLE_SHIFT * y),
                     U2S (current->y1 + DOUBLE_SHIFT * x),
                     U2S (current->x2 - DOUBLE_SHIFT * y),
                     U2S (current->y2 + DOUBLE_SHIFT * x));
          gdk_draw_line (pix, gc,
                     U2S (current->x1 + DOUBLE_SHIFT * y),
                     U2S (current->y1 - DOUBLE_SHIFT * x),
                     U2S (current->x2 + DOUBLE_SHIFT * y),
                     U2S (current->y2 - DOUBLE_SHIFT * x));
          break;

        case BOND_TRIPLE:
          r = hypot ((double) current->x2 - (double) current->x1,
                   (double) current->y2 - (double) current->y1);
          x = ((double) current->x2 - (double) current->x1) / r;
          y = ((double) current->y2 - (double) current->y1) / r;
          gdk_draw_line (pix, gc,
                     U2S (current->x1 - TRIPLE_SHIFT * y),
                     U2S (current->y1 + TRIPLE_SHIFT * x),
                     U2S (current->x2 - TRIPLE_SHIFT * y),
                     U2S (current->y2 + TRIPLE_SHIFT * x));
          gdk_draw_line (pix, gc,
                     U2S (current->x1 + TRIPLE_SHIFT * y),
                     U2S (current->y1 - TRIPLE_SHIFT * x),
                     U2S (current->x2 + TRIPLE_SHIFT * y),
                     U2S (current->y2 - TRIPLE_SHIFT * x));
          gdk_draw_line (pix, gc,
                     U2S (current->x1), U2S (current->y1),
                     U2S (current->x2), U2S (current->y2));
          break;

        case BOND_UP:
          r = hypot ((double) current->x2 - (double) current->x1,
                   (double) current->y2 - (double) current->y1);
          x = ((double) current->x2 - (double) current->x1) / r;
          y = ((double) current->y2 - (double) current->y1) / r;
          points[0].x = U2S (current->x2 - STEREO_SHIFT * y);
          points[0].y = U2S (current->y2 + STEREO_SHIFT * x);
          points[1].x = U2S (current->x2 + STEREO_SHIFT * y);
          points[1].y = U2S (current->y2 - STEREO_SHIFT * x);
          points[2].x = U2S (current->x1);
          points[2].y = U2S (current->y1);

          gdk_draw_polygon (pix, gc, TRUE, points, 3);
          break;

        case BOND_DOWN:
          r = hypot ((double) current->x2 - (double) current->x1,
                   (double) current->y2 - (double) current->y1);
          x = ((double) current->x2 - (double) current->x1) / r;
          y = ((double) current->y2 - (double) current->y1) / r;

          for (i = 0; i < 9; i++)
            {
            cx =
              current->x1 + (2 * i + 1) * (current->x2 -
                                     current->x1) / 18;
            cy =
              current->y1 + (2 * i + 1) * (current->y2 -
                                     current->y1) / 18;
            gdk_draw_line (pix, gc,
                         U2S (cx - STEREO_SHIFT * y * (i + 1) / 9),
                         U2S (cy + STEREO_SHIFT * x * (i + 1) / 9),
                         U2S (cx + STEREO_SHIFT * y * (i + 1) / 9),
                         U2S (cy - STEREO_SHIFT * x * (i + 1) / 9));
            }
          break;

        case BOND_DASHED:
          for (i = 0; i < 4; i++)
            {
            gdk_draw_line (pix, gc,
                         U2S (current->x1 +
                            2 * i * (current->x2 - current->x1) / 7),
                         U2S (current->y1 +
                            2 * i * (current->y2 - current->y1) / 7),
                         U2S (current->x1 +
                            (2 * i + 1) * (current->x2 -
                                       current->x1) / 7),
                         U2S (current->y1 +
                            (2 * i + 1) * (current->y2 -
                                       current->y1) / 7));
            }
          break;

        case BOND_ARROW:
        case BOND_ARC:
          if (current->type == BOND_ARROW ||
            (current->type == BOND_ARC && current->x3 == -1))
            {
            r = hypot ((double) current->x2 - (double) current->x1,
                     (double) current->y2 - (double) current->y1);
            x = ((double) current->x2 - (double) current->x1) / r;
            y = ((double) current->y2 - (double) current->y1) / r;
            gdk_draw_line (pix, gc, U2S (current->x1), U2S (current->y1),
                         U2S (current->x2), U2S (current->y2));
            }
          else
            {
            info_arc (&dx, &dy, &i, &j, &dir,
                    (double) current->x1,
                    (double) current->y1,
                    (double) current->x2,
                    (double) current->y2,
                    (double) current->x3, (double) current->y3);
            r = hypot ((double) current->x1 - dx,
                     (double) current->y1 - dy);
            gdk_draw_arc (pix, gc, FALSE, U2S (dx - r), U2S (dy - r),
                        U2S (2 * r), U2S (2 * r), i, j);
            }

          if (current->x4 != 0)
            {
            if (current->type == BOND_ARC)
              {
                y = dir * (dx - current->x2) / r;
                x = -dir * (dy - current->y2) / r;
              }

            switch (current->x4)
              {
              case 1:
                points[0].x = U2S (current->x2 - ARROW_SHIFT * y
                               - ARROW2_SHIFT * x);
                points[0].y = U2S (current->y2 + ARROW_SHIFT * x
                               - ARROW2_SHIFT * y);
                points[1].x = U2S (current->x2);
                points[1].y = U2S (current->y2);
                points[2].x = U2S (current->x2 + ARROW_SHIFT * y
                               - ARROW2_SHIFT * x);
                points[2].y = U2S (current->y2 - ARROW_SHIFT * x
                               - ARROW2_SHIFT * y);
                points[3].x = U2S (current->x2 - 0.7 * ARROW2_SHIFT * x);
                points[3].y = U2S (current->y2 - 0.7 * ARROW2_SHIFT * y);
                gdk_draw_polygon (pix, gc, TRUE, points, 4);
                break;

              case 2:
                points[1].x = U2S (current->x2);
                points[1].y = U2S (current->y2);
                points[2].x = U2S (current->x2 + ARROW_SHIFT * y
                               - ARROW2_SHIFT * x);
                points[2].y = U2S (current->y2 - ARROW_SHIFT * x
                               - ARROW2_SHIFT * y);
                points[3].x = U2S (current->x2 - 0.7 * ARROW2_SHIFT * x);
                points[3].y = U2S (current->y2 - 0.7 * ARROW2_SHIFT * y);
                gdk_draw_polygon (pix, gc, TRUE, points + 1, 3);
                break;

              case 3:
                points[0].x = U2S (current->x2 - ARROW_SHIFT * y
                               - ARROW2_SHIFT * x);
                points[0].y = U2S (current->y2 + ARROW_SHIFT * x
                               - ARROW2_SHIFT * y);
                points[1].x = U2S (current->x2);
                points[1].y = U2S (current->y2);
                points[2].x = U2S (current->x2 - 0.7 * ARROW2_SHIFT * x);
                points[2].y = U2S (current->y2 - 0.7 * ARROW2_SHIFT * y);
                gdk_draw_polygon (pix, gc, TRUE, points, 3);
                break;
              }
            }
          if (current->y4 != 0)
            {
            if (current->type == BOND_ARC)
              {
                y = dir * (dx - current->x1) / r;
                x = -dir * (dy - current->y1) / r;
              }
            switch (current->y4)
              {
              case 1:
                points[0].x = U2S (current->x1 + ARROW_SHIFT * y
                               + ARROW2_SHIFT * x);
                points[0].y = U2S (current->y1 - ARROW_SHIFT * x
                               + ARROW2_SHIFT * y);
                points[1].x = U2S (current->x1);
                points[1].y = U2S (current->y1);
                points[2].x = U2S (current->x1 - ARROW_SHIFT * y
                               + ARROW2_SHIFT * x);
                points[2].y = U2S (current->y1 + ARROW_SHIFT * x
                               + ARROW2_SHIFT * y);
                points[3].x = U2S (current->x1 + 0.7 * ARROW2_SHIFT * x);
                points[3].y = U2S (current->y1 + 0.7 * ARROW2_SHIFT * y);
                gdk_draw_polygon (pix, gc, TRUE, points, 4);
                break;

              case 3:
                points[0].x = U2S (current->x1 + ARROW_SHIFT * y
                               + ARROW2_SHIFT * x);
                points[0].y = U2S (current->y1 - ARROW_SHIFT * x
                               + ARROW2_SHIFT * y);
                points[1].x = U2S (current->x1);
                points[1].y = U2S (current->y1);
                points[2].x = U2S (current->x1 + 0.7 * ARROW2_SHIFT * x);
                points[2].y = U2S (current->y1 + 0.7 * ARROW2_SHIFT * y);
                gdk_draw_polygon (pix, gc, TRUE, points, 3);
                break;

              case 2:
                points[1].x = U2S (current->x1);
                points[1].y = U2S (current->y1);
                points[2].x = U2S (current->x1 - ARROW_SHIFT * y
                               + ARROW2_SHIFT * x);
                points[2].y = U2S (current->y1 + ARROW_SHIFT * x
                               + ARROW2_SHIFT * y);
                points[3].x = U2S (current->x1 + 0.7 * ARROW2_SHIFT * x);
                points[3].y = U2S (current->y1 + 0.7 * ARROW2_SHIFT * y);
                gdk_draw_polygon (pix, gc, TRUE, points + 1, 3);
                break;
              }
            }
          break;

        case BOND_CIRCLE:
          x = ((double) current->x1 + (double) current->x2) / 2;
          y = ((double) current->y1 + (double) current->y2) / 2;
          r = hypot ((double) current->x1 - (double) current->x2,
                   (double) current->y1 - (double) current->y2);
          gdk_draw_arc (pix, gc, FALSE, U2S (x - r / 2), U2S (y - r / 2),
                      U2S (r), U2S (r), 0, 23040);
          break;

        case BOND_GROUP:
          draw_bond_list (current->group_members, 1,
                        current->selected | selected, area, pix, mode,
                      zoom);
          break;

        case BOND_ATOM:
        case BOND_GROUP_L:
        case BOND_GROUP_R:
          break;

        default:
          bug_in ("draw_bond_list");

        }

      current = current->next;

      if (gc != gc_sel && gc != gc_gray)
        g_object_unref (gc);
      }

  current = list;
  if (current != NULL)
    while (current != NULL)
      {
      if (BOND_HAS_TEXT (current))
        {
          p_layout = gtk_widget_create_pango_layout (area, "");
          if ((current->selected & SEL_YES) || (selected & SEL_YES))
            pango_layout_set_rich_text (p_layout, current->pango, NULL,
                                          mode, current->width, zoom);
          else
          {
            pango_layout_set_rich_text (p_layout, current->pango,
                                      &(current->color), mode,
                                current->width, zoom);
          }
          
          gdk_draw_rectangle (pix, area->style->white_gc, TRUE,
                        U2S (current->x4) - 1, U2S (current->y4) - 1,
                        U2S (current->x2) + 3, U2S (current->y2) + 3);
          gdk_draw_layout (pix, area->style->black_gc,
                       U2S (current->x1), U2S (current->y1), p_layout);
          g_object_unref (p_layout);
        }

      current = current->next;
      }

/* Third loop, for ornaments (must be drawn over everything). */
  current = list;
  while (current != NULL)
  {
    if (mode == MODE_ORNAMENT)
      gc = area->style->black_gc;
    else
      gc = gc_gray;

    if (current->ornaments[0] != NULL)
      {
        if (BOND_HAS_TEXT (current))
        draw_ornaments (current->ornaments[0], current->x3, current->y3,
                        zoom, gc, pix);
        else
        draw_ornaments (current->ornaments[0], current->x1, current->y1,
                        zoom, gc, pix);
      }
    if (current->ornaments[1] != NULL)
      draw_ornaments (current->ornaments[1], current->x2, current->y2,
                    zoom, gc, pix);

    current = current->next;
  }
}

/* This routine is used to determine the bouding box of a given pixmap */
void
determine_bbox_from_pixmap (GdkPixmap *pix)
{
  GdkPixbuf * pixbuf;

  pixbuf = gdk_pixbuf_get_from_drawable (NULL, GDK_DRAWABLE (pix), NULL,
                                         0, 0, 0, 0, -1, -1);
  if (pixbuf == NULL)
    bug_in ("determine_bbox_from_pixmap");
}


Generated by  Doxygen 1.6.0   Back to index