beziershape.c 38.3 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
/* Dia -- an diagram creation/manipulation program
 * Copyright (C) 1999 Alexander Larsson
 *
 * beziershape.c - code to help implement bezier shapes
 * Copyright (C) 2000 James Henstridge
 *
 * This program is free software; you can 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 of the License, or
 * (at your option) any later version.
 *
 * This program 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
18 19
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 21
 */

Lars Clausen's avatar
Lars Clausen committed
22 23
#include <config.h>

24 25 26
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
27
#include <string.h> /* memcpy() */
28 29

#include "beziershape.h"
Hans Breuer's avatar
Hans Breuer committed
30
#include "diarenderer.h"
31 32 33 34 35 36 37 38 39 40

#define HANDLE_BEZMAJOR  (HANDLE_CUSTOM1)
#define HANDLE_LEFTCTRL  (HANDLE_CUSTOM2)
#define HANDLE_RIGHTCTRL (HANDLE_CUSTOM3)

enum change_type {
  TYPE_ADD_POINT,
  TYPE_REMOVE_POINT
};

41 42
/* Invariant:
   # of handles = 3*(numpoints-1)
43 44
   # of connections = 2*(numpoints-1) + 1 (main point)
   For historical reasons, the main point is the last cp.
45
 */
46
struct BezPointChange {
47 48 49 50 51 52 53 54 55 56 57 58
  ObjectChange obj_change;

  enum change_type type;
  int applied;
  
  BezPoint point;
  BezCornerType corner_type;
  int pos;

  /* owning ref when not applied for ADD_POINT
   * owning ref when applied for REMOVE_POINT */
  Handle *handle1, *handle2, *handle3;
59
  ConnectionPoint *cp1, *cp2;
60 61 62 63 64 65 66 67 68 69 70 71 72 73
};

struct CornerChange {
  ObjectChange obj_change;
  /* Only one kind of corner_change */
  int applied;

  Handle *handle;
  /* Old places when SET_CORNER_TYPE is applied */
  Point point_left, point_right;
  BezCornerType old_type, new_type;
};

static ObjectChange *
74 75 76 77 78 79 80 81 82 83
beziershape_create_point_change(BezierShape *bezier,
				enum change_type type,
				BezPoint *point,
				BezCornerType corner_type,
				int segment,
				Handle *handle1,
				Handle *handle2,
				Handle *handle3,
				ConnectionPoint *cp1,
				ConnectionPoint *cp2);
84
static ObjectChange *
85 86 87 88 89 90
beziershape_create_corner_change(BezierShape *bezier,
				 Handle *handle,
				 Point *point_left,
				 Point *point_right,
				 BezCornerType old_corner_type,
				 BezCornerType new_corner_type);
91

92 93
static void new_handles_and_connections(BezierShape *bezier, int num_points);

94 95 96 97
/** Set up a handle for any part of a bezier
 * @param handle A handle to set up.
 * @param id Handle id (HANDLE_BEZMAJOR or HANDLE_RIGHTCTRL or HANDLE_LEFTCTRL)
 */
98
static void
99
setup_handle(Handle *handle, HandleId id)
100
{
101
  handle->id = id;
102
  handle->type =
103
    (id == HANDLE_BEZMAJOR) ?
104 105 106 107 108 109
    HANDLE_MAJOR_CONTROL :
    HANDLE_MINOR_CONTROL;
  handle->connect_type = HANDLE_NONCONNECTABLE;
  handle->connected_to = NULL;
}

110 111 112 113 114 115
/** Get the number in the array of handles that a given handle has.
 * @param bezier A bezier object with handles set up.
 * @param handle A handle object.
 * @returns The index in bezier->object.handles of the handle object, or -1 if
 *          `handle' is not in the array.
 */
116 117
static int
get_handle_nr (BezierShape *bezier, Handle *handle)
118 119 120 121 122 123 124 125 126
{
  int i = 0;
  for (i = 0; i < bezier->object.num_handles; i++) {
    if (bezier->object.handles[i] == handle)
      return i;
  }
  return -1;
}

127 128
#define get_comp_nr(hnum) ((int)(hnum)/3+1)
#define get_major_nr(hnum) (((int)(hnum)+2)/3)
129

130 131 132 133 134 135 136 137 138 139 140 141 142
/*!
 * \brief Move one of the handles associated with the
 * @param bezier The object whose handle is being moved.
 * @param handle The handle being moved.
 * @param to The position it has been moved to (corrected for
 *   vertical/horizontal only movement).
 * @param cp If non-NULL, the connectionpoint found at this position.
 *   If @a cp is NULL, there may or may not be a connectionpoint.
 * @param reason ignored
 * @param modifiers ignored
 * @return NULL
 * \memberof BezierShape
 */
143
ObjectChange *
144 145 146 147 148 149
beziershape_move_handle (BezierShape *bezier,
			 Handle *handle,
			 Point *to,
			 ConnectionPoint *cp,
			 HandleMoveReason reason,
			 ModifierKeys modifiers)
150
{
151 152
  int handle_nr, comp_nr;
  int next_nr, prev_nr;
153 154 155 156 157 158 159 160 161
  Point delta, pt;
  
  delta = *to;
  point_sub(&delta, &handle->pos);

  handle_nr = get_handle_nr(bezier, handle);
  comp_nr = get_comp_nr(handle_nr);
  next_nr = comp_nr + 1;
  prev_nr = comp_nr - 1;
162
  if (comp_nr == bezier->bezier.num_points - 1)
163 164
    next_nr = 1;
  if (comp_nr == 1)
165
    prev_nr = bezier->bezier.num_points - 1;
166 167 168
  
  switch(handle->id) {
  case HANDLE_BEZMAJOR:
169 170 171 172 173
    if (comp_nr == bezier->bezier.num_points - 1) {
      bezier->bezier.points[comp_nr].p3 = *to;
      bezier->bezier.points[0].p1 = bezier->bezier.points[0].p3 = *to;
      point_add(&bezier->bezier.points[comp_nr].p2, &delta);
      point_add(&bezier->bezier.points[1].p1, &delta);
174
    } else {
175 176 177
      bezier->bezier.points[comp_nr].p3 = *to;
      point_add(&bezier->bezier.points[comp_nr].p2, &delta);
      point_add(&bezier->bezier.points[comp_nr+1].p1, &delta);
178 179 180
    }
    break;
  case HANDLE_LEFTCTRL:
181 182
    bezier->bezier.points[comp_nr].p2 = *to;
    switch (bezier->bezier.corner_types[comp_nr]) {
183
    case BEZ_CORNER_SYMMETRIC:
184 185 186 187
      pt = bezier->bezier.points[comp_nr].p3;
      point_sub(&pt, &bezier->bezier.points[comp_nr].p2);
      point_add(&pt, &bezier->bezier.points[comp_nr].p3);
      bezier->bezier.points[next_nr].p1 = pt;
188 189 190 191
      break;
    case BEZ_CORNER_SMOOTH: {
      real len;

192 193
      pt = bezier->bezier.points[next_nr].p1;
      point_sub(&pt, &bezier->bezier.points[comp_nr].p3);
194 195
      len = point_len(&pt);

196 197
      pt = bezier->bezier.points[comp_nr].p3;
      point_sub(&pt, &bezier->bezier.points[comp_nr].p2);
198 199 200 201 202 203
      if (point_len(&pt) > 0)
	point_normalize(&pt);
      else {
	pt.x = 1.0; pt.y = 0.0;
      }
      point_scale(&pt, len);
204 205
      point_add(&pt, &bezier->bezier.points[comp_nr].p3);
      bezier->bezier.points[next_nr].p1 = pt;
206 207 208 209 210 211 212 213
      break;
    }
    case BEZ_CORNER_CUSP:
      /* no mirror point movement required */
      break;
    }
    break;
  case HANDLE_RIGHTCTRL:
214 215
    bezier->bezier.points[comp_nr].p1 = *to;
    switch (bezier->bezier.corner_types[prev_nr]) {
216
    case BEZ_CORNER_SYMMETRIC:
217 218 219 220
      pt = bezier->bezier.points[prev_nr].p3;
      point_sub(&pt, &bezier->bezier.points[comp_nr].p1);
      point_add(&pt, &bezier->bezier.points[prev_nr].p3);
      bezier->bezier.points[prev_nr].p2 = pt;
221 222 223 224
      break;
    case BEZ_CORNER_SMOOTH: {
      real len;

225 226
      pt = bezier->bezier.points[prev_nr].p2;
      point_sub(&pt, &bezier->bezier.points[prev_nr].p3);
227 228
      len = point_len(&pt);

229 230
      pt = bezier->bezier.points[prev_nr].p3;
      point_sub(&pt, &bezier->bezier.points[comp_nr].p1);
231 232 233 234 235 236
      if (point_len(&pt) > 0)
	point_normalize(&pt);
      else {
	pt.x = 1.0; pt.y = 0.0;
      }
      point_scale(&pt, len);
237 238
      point_add(&pt, &bezier->bezier.points[prev_nr].p3);
      bezier->bezier.points[prev_nr].p2 = pt;
239 240 241 242 243 244 245 246
      break;
    }
    case BEZ_CORNER_CUSP:
      /* no mirror point movement required */
      break;
    }
    break;
  default:
247
    g_warning("Internal error in beziershape_move_handle.");
248 249
    break;
  }
250
  return NULL;
251 252
}

253 254 255 256 257 258 259 260
/*!
 * \brief Move the entire object.
 * @param bezier The object being moved.
 * @param to Where the object is being moved to.  This is the first point
 * of the points array.
 * @return NULL
 * \memberof _BezierConn
 */
261
ObjectChange*
262
beziershape_move (BezierShape *bezier, Point *to)
263 264 265 266 267
{
  Point p;
  int i;
  
  p = *to;
268
  point_sub(&p, &bezier->bezier.points[0].p1);
269

270 271 272 273 274
  bezier->bezier.points[0].p1 = bezier->bezier.points[0].p3 = *to;
  for (i = 1; i < bezier->bezier.num_points; i++) {
    point_add(&bezier->bezier.points[i].p1, &p);
    point_add(&bezier->bezier.points[i].p2, &p);
    point_add(&bezier->bezier.points[i].p3, &p);
275
  }
276 277

  return NULL;
278 279
}

280 281 282 283 284 285 286 287
/*!
 * \brief Return the handle closest to a given point.
 * @param bezier A bezier object
 * @param point A point to find distances from
 * @return The handle on `bezier' closest to `point'.
 *
 * \memberof _BezierShape
 */
288
Handle *
289 290
beziershape_closest_handle (BezierShape *bezier,
			    Point *point)
291 292
{
  int i, hn;
293
  real dist = G_MAXDOUBLE;
294
  Handle *closest = NULL;
295
  
296
  for (i = 1, hn = 0; i < bezier->bezier.num_points; i++, hn++) {
297 298
    real new_dist;

299
    new_dist = distance_point_point(point, &bezier->bezier.points[i].p1);
300 301
    if (new_dist < dist) {
      dist = new_dist;
302
      closest = bezier->object.handles[hn];
303 304 305
    }
    hn++;

306
    new_dist = distance_point_point(point, &bezier->bezier.points[i].p2);
307 308
    if (new_dist < dist) {
      dist = new_dist;
309
      closest = bezier->object.handles[hn];
310
    }
311
    hn++;
312

313
    new_dist = distance_point_point(point, &bezier->bezier.points[i].p3);
314 315
    if (new_dist < dist) {
      dist = new_dist;
316
      closest = bezier->object.handles[hn];
317 318 319 320 321 322
    }
  }
  return closest;
}

Handle *
323
beziershape_closest_major_handle (BezierShape *bezier, Point *point)
324 325 326 327
{
  Handle *closest = beziershape_closest_handle(bezier, point);
  int pos = get_major_nr(get_handle_nr(bezier, closest));

328
  if (pos == 0)
329
    pos = bezier->bezier.num_points - 1;
330
  return bezier->object.handles[3*pos - 1];
331 332
}

333 334 335 336 337 338 339 340
/*!
 * \brief Return the distance from a bezier to a point.
 * @param bezier A bezier object.
 * @param point A point to compare with.
 * @param line_width The line width of the bezier line.
 * @return The shortest distance from the point to any part of the bezier.
 * \memberof _BezierShape
 */
341
real
342
beziershape_distance_from (BezierShape *bezier, Point *point, real line_width)
343
{
344
  return distance_bez_shape_point(bezier->bezier.points, bezier->bezier.num_points,
345 346 347 348
				  line_width, point);
}

static void
349 350 351 352 353
add_handles (BezierShape *bezier,
	     int pos, BezPoint *point,
	     BezCornerType corner_type,
	     Handle *handle1, Handle *handle2, Handle *handle3,
	     ConnectionPoint *cp1, ConnectionPoint *cp2)
354 355
{
  int i, next;
356
  DiaObject *obj = &bezier->object;
357

358
  g_assert(pos >= 1);
359
  g_assert(pos <= bezier->bezier.num_points);
360

361
  bezier->bezier.num_points++;
362
  next = pos + 1;
363 364
  bezier->bezier.points = g_realloc(bezier->bezier.points, bezier->bezier.num_points*sizeof(BezPoint));
  if (pos == bezier->bezier.num_points - 1)
365
    next = 1;
366 367
  bezier->bezier.corner_types = g_realloc(bezier->bezier.corner_types,
				   bezier->bezier.num_points * sizeof(BezCornerType));
368

369 370 371
  for (i = bezier->bezier.num_points - 1; i > pos; i--) {
    bezier->bezier.points[i] = bezier->bezier.points[i-1];
    bezier->bezier.corner_types[i] = bezier->bezier.corner_types[i-1];
372
  }
373 374 375 376 377 378
  bezier->bezier.points[pos] = *point;
  bezier->bezier.points[pos].p1 = bezier->bezier.points[next].p1;
  bezier->bezier.points[next].p1 = point->p1;
  if (pos == bezier->bezier.num_points - 1)
    bezier->bezier.points[0].p1 = bezier->bezier.points[0].p3 = bezier->bezier.points[pos].p3;
  bezier->bezier.corner_types[pos] = corner_type;
379 380 381 382 383
  object_add_handle_at(obj, handle1, 3*pos-3);
  object_add_handle_at(obj, handle2, 3*pos-2);
  object_add_handle_at(obj, handle3, 3*pos-1);
  object_add_connectionpoint_at(obj, cp1, 2*pos-2);
  object_add_connectionpoint_at(obj, cp2, 2*pos-1);
384 385 386
}

static void
387
remove_handles (BezierShape *bezier, int pos)
388 389
{
  int i;
390
  DiaObject *obj;
391
  Handle *old_handle1, *old_handle2, *old_handle3;
392
  ConnectionPoint *old_cp1, *old_cp2;
393
  Point tmppoint;
394
  Point controlvector;
395 396
  controlvector.x=0;
  controlvector.y=0;
397

398
  g_assert(pos > 0);
399
  g_assert(pos < bezier->bezier.num_points);
400

401
  obj = (DiaObject *)bezier;
402 403

  /* delete the points */
404 405 406 407 408
  bezier->bezier.num_points--;
  tmppoint = bezier->bezier.points[pos].p1;
  if (pos == bezier->bezier.num_points) {
    controlvector = bezier->bezier.points[pos-1].p3;
    point_sub(&controlvector, &bezier->bezier.points[pos].p1);
409
  }
410 411 412
  for (i = pos; i < bezier->bezier.num_points; i++) {
    bezier->bezier.points[i] = bezier->bezier.points[i+1];
    bezier->bezier.corner_types[i] = bezier->bezier.corner_types[i+1];
413
  }
414 415
  bezier->bezier.points[pos].p1 = tmppoint;
  if (pos == bezier->bezier.num_points) {
416 417
    /* If this was the last point, we also need to move points[0] and
       the control point in points[1]. */
418 419 420
    bezier->bezier.points[0].p1 = bezier->bezier.points[bezier->bezier.num_points-1].p3;
    bezier->bezier.points[1].p1 = bezier->bezier.points[0].p1;
    point_sub(&bezier->bezier.points[1].p1, &controlvector);
421
  }
422 423 424 425
  bezier->bezier.points = g_realloc(bezier->bezier.points,
			     bezier->bezier.num_points * sizeof(BezPoint));
  bezier->bezier.corner_types = g_realloc(bezier->bezier.corner_types,
				bezier->bezier.num_points * sizeof(BezCornerType));
426

427 428 429
  old_handle1 = obj->handles[3*pos-3];
  old_handle2 = obj->handles[3*pos-2];
  old_handle3 = obj->handles[3*pos-1];
430 431 432
  object_remove_handle(&bezier->object, old_handle1);
  object_remove_handle(&bezier->object, old_handle2);
  object_remove_handle(&bezier->object, old_handle3);
433 434
  old_cp1 = obj->connections[2*pos-2];
  old_cp2 = obj->connections[2*pos-1];
435 436
  object_remove_connectionpoint(&bezier->object, old_cp1);
  object_remove_connectionpoint(&bezier->object, old_cp2);
437 438 439 440 441 442
}


/* Add a point by splitting segment into two, putting the new point at
 'point' or, if NULL, in the middle */
ObjectChange *
443 444
beziershape_add_segment (BezierShape *bezier,
			 int segment, Point *point)
445 446 447 448
{
  BezPoint realpoint;
  BezCornerType corner_type = BEZ_CORNER_SYMMETRIC;
  Handle *new_handle1, *new_handle2, *new_handle3;
449
  ConnectionPoint *new_cp1, *new_cp2;
450
  Point startpoint;
451
  Point other;
452

453 454
  g_return_val_if_fail (segment >= 0 && segment < bezier->bezier.num_points, NULL);

455 456
  if (bezier->bezier.points[segment].type == BEZ_CURVE_TO)
    startpoint = bezier->bezier.points[segment].p3;
457
  else 
458 459
    startpoint = bezier->bezier.points[segment].p1;
  other = bezier->bezier.points[segment+1].p3;
460
  if (point == NULL) {
461 462 463 464 465 466
    realpoint.p1.x = (startpoint.x + other.x)/6;
    realpoint.p1.y = (startpoint.y + other.y)/6;
    realpoint.p2.x = (startpoint.x + other.x)/3;
    realpoint.p2.y = (startpoint.y + other.y)/3;
    realpoint.p3.x = (startpoint.x + other.x)/2;
    realpoint.p3.y = (startpoint.y + other.y)/2;
467
  } else {
468 469 470
    realpoint.p2.x = point->x+(startpoint.x-other.x)/6;
    realpoint.p2.y = point->y+(startpoint.y-other.y)/6;

471 472
    realpoint.p3 = *point;
    /* this really goes into the next segment ... */
473 474
    realpoint.p1.x = point->x-(startpoint.x-other.x)/6;
    realpoint.p1.y = point->y-(startpoint.y-other.y)/6;
475 476 477
  }
  realpoint.type = BEZ_CURVE_TO;

478 479 480
  new_handle1 = g_new0(Handle,1);
  new_handle2 = g_new0(Handle,1);
  new_handle3 = g_new0(Handle,1);
481 482 483
  setup_handle(new_handle1, HANDLE_RIGHTCTRL);
  setup_handle(new_handle2, HANDLE_LEFTCTRL);
  setup_handle(new_handle3, HANDLE_BEZMAJOR);
484 485 486 487
  new_cp1 = g_new0(ConnectionPoint, 1);
  new_cp2 = g_new0(ConnectionPoint, 1);
  new_cp1->object = &bezier->object;
  new_cp2->object = &bezier->object;
488
  add_handles(bezier, segment+1, &realpoint, corner_type,
489
	      new_handle1, new_handle2, new_handle3, new_cp1, new_cp2);
490
  return beziershape_create_point_change(bezier, TYPE_ADD_POINT,
491
					 &realpoint, corner_type, segment+1,
492 493
					 new_handle1, new_handle2, new_handle3,
					 new_cp1, new_cp2);
494 495
}

496 497 498 499 500 501 502
/*!
 * \brief Remove a segment from a bezier.
 * @param bezier The bezier to remove a segment from.
 * @param pos The index of the segment to remove.
 * @returns Undo information for the segment removal.
 * \memberof _BezierShape
 */
503
ObjectChange *
504
beziershape_remove_segment (BezierShape *bezier, int pos)
505 506
{
  Handle *old_handle1, *old_handle2, *old_handle3;
507
  ConnectionPoint *old_cp1, *old_cp2;
508 509
  BezPoint old_point;
  BezCornerType old_ctype;
510
  int next = pos+1;
511

512
  g_return_val_if_fail (pos > 0 && pos < bezier->bezier.num_points, NULL);
513
  g_assert(bezier->bezier.num_points > 2);
514

515
  if (pos == bezier->bezier.num_points - 1)
516
    next = 1;
517 518
  else if (next == 1)
    next = bezier->bezier.num_points - 1;
519

520 521 522
  old_handle1 = bezier->object.handles[3*pos-3];
  old_handle2 = bezier->object.handles[3*pos-2];
  old_handle3 = bezier->object.handles[3*pos-1];
523
  old_point = bezier->bezier.points[pos];
524
  /* remember the old control point of following bezpoint */
525 526
  old_point.p1 = bezier->bezier.points[next].p1;
  old_ctype = bezier->bezier.corner_types[pos];
527

528 529
  old_cp1 = bezier->object.connections[2*pos-2];
  old_cp2 = bezier->object.connections[2*pos-1];
530
  
531 532 533
  object_unconnect((DiaObject *)bezier, old_handle1);
  object_unconnect((DiaObject *)bezier, old_handle2);
  object_unconnect((DiaObject *)bezier, old_handle3);
534 535 536 537 538 539 540

  remove_handles(bezier, pos);

  beziershape_update_data(bezier);
  
  return beziershape_create_point_change(bezier, TYPE_REMOVE_POINT,
					 &old_point, old_ctype, pos,
541 542
					 old_handle1, old_handle2, old_handle3,
					 old_cp1, old_cp2);
543 544
}

545 546 547 548 549 550 551 552 553 554
/*!
 * \brief Limit movability of control handles
 *
 * Update a corner to have less freedom in its control handles, arranging
 * the control points at some reasonable places.
 * @param bezier A bezierconn to straighten a corner of
 * @param comp_nr The index into the corner_types array of the corner to
 *                straighten.
 * \memberof _BezierShape
 */
555
static void
556
beziershape_straighten_corner (BezierShape *bezier, int comp_nr)
557
{
558 559
  int next_nr;

560
  if (comp_nr == 0) comp_nr = bezier->bezier.num_points - 1;
561
  next_nr = comp_nr + 1;
562
  if (comp_nr == bezier->bezier.num_points - 1)
563 564 565 566
    next_nr = 1;
  /* Neat thing would be to have the kind of straigthening depend on
     which handle was chosen:  Mid-handle does average, other leaves that
     handle where it is. */
567 568
  bezier->bezier.points[0].p3 = bezier->bezier.points[0].p1;
  switch (bezier->bezier.corner_types[comp_nr]) {
569 570 571
  case BEZ_CORNER_SYMMETRIC: {
    Point pt1, pt2;

572 573 574 575
    pt1 = bezier->bezier.points[comp_nr].p3;
    point_sub(&pt1, &bezier->bezier.points[comp_nr].p2);
    pt2 = bezier->bezier.points[comp_nr].p3;
    point_sub(&pt2, &bezier->bezier.points[next_nr].p1);
576 577 578 579 580
    point_scale(&pt2, -1.0);
    point_add(&pt1, &pt2);
    point_scale(&pt1, 0.5);
    pt2 = pt1;
    point_scale(&pt1, -1.0);
581 582 583 584
    point_add(&pt1, &bezier->bezier.points[comp_nr].p3);
    point_add(&pt2, &bezier->bezier.points[comp_nr].p3);
    bezier->bezier.points[comp_nr].p2 = pt1;
    bezier->bezier.points[next_nr].p1 = pt2;
585
    beziershape_update_data(bezier);
586 587 588 589 590
  }
  break;
  case BEZ_CORNER_SMOOTH: {
    Point pt1, pt2;
    real len1, len2;
591 592 593 594
    pt1 = bezier->bezier.points[comp_nr].p3;
    point_sub(&pt1, &bezier->bezier.points[comp_nr].p2);
    pt2 = bezier->bezier.points[comp_nr].p3;
    point_sub(&pt2, &bezier->bezier.points[next_nr].p1);
595 596 597 598 599 600 601 602 603 604 605
    len1 = point_len(&pt1);
    len2 = point_len(&pt2);
    point_scale(&pt2, -1.0);
    if (len1 > 0)
      point_normalize(&pt1);
    if (len2 > 0)
      point_normalize(&pt2);
    point_add(&pt1, &pt2);
    point_scale(&pt1, 0.5);
    pt2 = pt1;
    point_scale(&pt1, -len1);
606
    point_add(&pt1, &bezier->bezier.points[comp_nr].p3);
607
    point_scale(&pt2, len2);
608 609 610
    point_add(&pt2, &bezier->bezier.points[comp_nr].p3);
    bezier->bezier.points[comp_nr].p2 = pt1;
    bezier->bezier.points[next_nr].p1 = pt2;
611
    beziershape_update_data(bezier);
612 613 614 615 616
  }
    break;
  case BEZ_CORNER_CUSP:
    break;
  }
617
  bezier->bezier.points[0].p1 = bezier->bezier.points[0].p3;
618 619 620
}

ObjectChange *
621
beziershape_set_corner_type (BezierShape *bezier,
622 623
			     Handle *handle,
			     BezCornerType corner_type)
624 625 626 627 628 629
{
  Handle *mid_handle = NULL;
  Point old_left, old_right;
  int old_type;
  int handle_nr, comp_nr;

630
  handle_nr = get_handle_nr(bezier, handle);
631 632 633 634 635 636 637

  switch (handle->id) {
  case HANDLE_BEZMAJOR:
    mid_handle = handle;
    break;
  case HANDLE_LEFTCTRL:
    handle_nr++;
638 639
    if (handle_nr == bezier->object.num_handles) handle_nr = 0;
    mid_handle = bezier->object.handles[handle_nr];
640 641 642
    break;
  case HANDLE_RIGHTCTRL:
    handle_nr--;
643 644
    if (handle_nr < 0) handle_nr = bezier->object.num_handles - 1;
    mid_handle = bezier->object.handles[handle_nr];
645 646 647 648 649 650 651 652
    break;
  default:
    g_assert_not_reached();
    break;
  }

  comp_nr = get_major_nr(handle_nr);

653 654 655 656
  old_type = bezier->bezier.corner_types[comp_nr];
  old_left = bezier->bezier.points[comp_nr].p2;
  if (comp_nr == bezier->bezier.num_points - 1)
    old_right = bezier->bezier.points[1].p1;
657
  else
658
    old_right = bezier->bezier.points[comp_nr+1].p1;
659

660 661 662 663 664
#if 0
  g_message("Setting corner type on segment %d to %s", comp_nr,
	    corner_type == BEZ_CORNER_SYMMETRIC ? "symmetric" :
	    corner_type == BEZ_CORNER_SMOOTH ? "smooth" : "cusp");
#endif
665
  bezier->bezier.corner_types[comp_nr] = corner_type;
666
  if (comp_nr == 0)
667 668 669
    bezier->bezier.corner_types[bezier->bezier.num_points-1] = corner_type;
  else if (comp_nr == bezier->bezier.num_points - 1)
    bezier->bezier.corner_types[0] = corner_type;
670

671
  beziershape_straighten_corner(bezier, comp_nr);
672

673
  return beziershape_create_corner_change(bezier, mid_handle, &old_left,
674 675 676 677
					  &old_right, old_type, corner_type);
}

void
678
beziershape_update_data (BezierShape *bezier)
679 680
{
  int i;
681
  Point last;
682
  Point minp, maxp;
683
  
684
  DiaObject *obj = &bezier->object;
685 686
  
  /* handle the case of whole points array update (via set_prop) */
687 688
  if (3*(bezier->bezier.num_points-1) != obj->num_handles ||
      2*(bezier->bezier.num_points-1) + 1 != obj->num_connections) {
689 690 691 692 693 694
    object_unconnect_all(obj); /* too drastic ? */

    /* delete the old ones */
    for (i = 0; i < obj->num_handles; i++)
      g_free(obj->handles[i]);
    g_free(obj->handles);
695
    for (i = 0; i < obj->num_connections; i++)
696 697 698
      g_free(obj->connections[i]);
    g_free(obj->connections);

699
    obj->num_handles = 3*(bezier->bezier.num_points-1);
700
    obj->handles = g_new(Handle*, obj->num_handles);
701
    obj->num_connections = 2*(bezier->bezier.num_points-1) + 1;
702 703
    obj->connections = g_new(ConnectionPoint *, obj->num_connections);

704
    new_handles_and_connections(bezier, bezier->bezier.num_points);
705

706 707 708
    bezier->bezier.corner_types = g_realloc(bezier->bezier.corner_types, bezier->bezier.num_points*sizeof(BezCornerType));
    for (i = 0; i < bezier->bezier.num_points; i++)
      bezier->bezier.corner_types[i] = BEZ_CORNER_SYMMETRIC;
709 710
  }

711
  /* Update handles: */
712 713 714 715 716
  bezier->bezier.points[0].p3 = bezier->bezier.points[0].p1;
  for (i = 1; i < bezier->bezier.num_points; i++) {
    obj->handles[3*i-3]->pos = bezier->bezier.points[i].p1;
    obj->handles[3*i-2]->pos = bezier->bezier.points[i].p2;
    obj->handles[3*i-1]->pos = bezier->bezier.points[i].p3;
717
  }
718 719

  /* Update connection points: */
720 721
  last = bezier->bezier.points[0].p1;
  for (i = 1; i < bezier->bezier.num_points; i++) {
722
    Point slopepoint1, slopepoint2;
723
    slopepoint1 = bezier->bezier.points[i].p1;
724 725 726
    point_sub(&slopepoint1, &last);
    point_scale(&slopepoint1, .5);
    point_add(&slopepoint1, &last);
727 728
    slopepoint2 = bezier->bezier.points[i].p2;
    point_sub(&slopepoint2, &bezier->bezier.points[i].p3);
729
    point_scale(&slopepoint2, .5);
730
    point_add(&slopepoint2, &bezier->bezier.points[i].p3);
731

732 733
    obj->connections[2*i-2]->pos = last;
    obj->connections[2*i-2]->directions =
734
      find_slope_directions(last, bezier->bezier.points[i].p1);
735 736 737 738 739 740 741 742 743 744 745
    if (bezier->bezier.points[i].type == BEZ_CURVE_TO) {
      obj->connections[2*i-1]->pos.x =
        (last.x + 3*bezier->bezier.points[i].p1.x + 3*bezier->bezier.points[i].p2.x +
         bezier->bezier.points[i].p3.x)/8;
      obj->connections[2*i-1]->pos.y =
        (last.y + 3*bezier->bezier.points[i].p1.y + 3*bezier->bezier.points[i].p2.y +
         bezier->bezier.points[i].p3.y)/8;
    } else {
      obj->connections[2*i-1]->pos.x = (last.x + bezier->bezier.points[i].p1.x) / 2;
      obj->connections[2*i-1]->pos.y = (last.y + bezier->bezier.points[i].p1.y) / 2;
    }
746
    obj->connections[2*i-1]->directions = 
747
      find_slope_directions(slopepoint1, slopepoint2);
748 749 750 751
    if (bezier->bezier.points[i].type == BEZ_CURVE_TO)
      last = bezier->bezier.points[i].p3;
    else
      last = bezier->bezier.points[i].p1;
752
  }
753 754
  
  /* Find the middle of the object (or some approximation at least) */
755 756 757
  minp = maxp = bezier->bezier.points[0].p1;
  for (i = 1; i < bezier->bezier.num_points; i++) {
    Point p = bezier->bezier.points[i].p3;
758 759 760 761 762 763 764 765
    if (p.x < minp.x) minp.x = p.x;
    if (p.x > maxp.x) maxp.x = p.x;
    if (p.y < minp.y) minp.y = p.y;
    if (p.y > maxp.y) maxp.y = p.y;
  }
  obj->connections[obj->num_connections-1]->pos.x = (minp.x + maxp.x) / 2;
  obj->connections[obj->num_connections-1]->pos.y = (minp.y + maxp.y) / 2;
  obj->connections[obj->num_connections-1]->directions = DIR_ALL;
766 767 768
}

void
769
beziershape_update_boundingbox (BezierShape *bezier)
770
{
771 772 773
  ElementBBExtras *extra;
  PolyBBExtras pextra;

Hans Breuer's avatar
Hans Breuer committed
774
  g_assert(bezier != NULL);
775

776
  extra = &bezier->extra_spacing;
777 778 779 780
  pextra.start_trans = pextra.end_trans = 
    pextra.start_long = pextra.end_long = 0;
  pextra.middle_trans = extra->border_trans;

781 782
  polybezier_bbox(&bezier->bezier.points[0],
                  bezier->bezier.num_points,
783 784
                  &pextra, TRUE,
                  &bezier->object.bounding_box);
785 786
}

787
static void
788
new_handles_and_connections (BezierShape *bezier, int num_points)
789
{
790
  DiaObject *obj;
Lars Clausen's avatar
Lars Clausen committed
791
  int i;
792 793 794

  obj = &bezier->object;

Lars Clausen's avatar
Lars Clausen committed
795
  for (i = 0; i < num_points-1; i++) {
796 797 798
    obj->handles[3*i] = g_new0(Handle,1);
    obj->handles[3*i+1] = g_new0(Handle,1);
    obj->handles[3*i+2] = g_new0(Handle,1);
799
  
Lars Clausen's avatar
Lars Clausen committed
800 801 802 803 804 805 806 807 808
    obj->handles[3*i]->connect_type = HANDLE_NONCONNECTABLE;
    obj->handles[3*i]->connected_to = NULL;
    obj->handles[3*i]->type = HANDLE_MINOR_CONTROL;
    obj->handles[3*i]->id = HANDLE_RIGHTCTRL;

    obj->handles[3*i+1]->connect_type = HANDLE_NONCONNECTABLE;
    obj->handles[3*i+1]->connected_to = NULL;
    obj->handles[3*i+1]->type = HANDLE_MINOR_CONTROL;
    obj->handles[3*i+1]->id = HANDLE_LEFTCTRL;
809
  
Lars Clausen's avatar
Lars Clausen committed
810 811 812 813 814 815 816 817 818
    obj->handles[3*i+2]->connect_type = HANDLE_NONCONNECTABLE;
    obj->handles[3*i+2]->connected_to = NULL;
    obj->handles[3*i+2]->type = HANDLE_MAJOR_CONTROL;
    obj->handles[3*i+2]->id = HANDLE_BEZMAJOR;

    obj->connections[2*i] = g_new0(ConnectionPoint, 1);
    obj->connections[2*i+1] = g_new0(ConnectionPoint, 1);
    obj->connections[2*i]->object = obj;
    obj->connections[2*i+1]->object = obj;
819 820
    obj->connections[2*i]->flags = 0;
    obj->connections[2*i+1]->flags = 0;
Lars Clausen's avatar
Lars Clausen committed
821
  }
822 823 824 825 826

  /** Main point */
  obj->connections[obj->num_connections-1] = g_new0(ConnectionPoint, 1);
  obj->connections[obj->num_connections-1]->object = obj;
  obj->connections[obj->num_connections-1]->flags = CP_FLAGS_MAIN;
827 828
}

829 830 831 832 833 834 835
/** Initialize a bezier object with the given amount of points.
 * The points array of the bezier object might not be previously 
 * initialized with appropriate positions.
 * This will set up handles and make all corners symmetric.
 * @param bezier A newly allocated beziershape object.
 * @param num_points The initial number of points on the curve.
 */
836
void
837
beziershape_init (BezierShape *bezier, int num_points)
838
{
839
  DiaObject *obj;
840 841 842 843
  int i;

  obj = &bezier->object;

844
  object_init(obj, 3*(num_points-1), 2*(num_points-1) + 1);
845
  
846
  bezier->bezier.num_points = num_points;
847

848 849 850 851
  bezier->bezier.points = g_new(BezPoint, num_points);
  bezier->bezier.corner_types = g_new(BezCornerType, num_points);
  bezier->bezier.points[0].type = BEZ_MOVE_TO;
  bezier->bezier.corner_types[0] = BEZ_CORNER_SYMMETRIC;
852
  for (i = 1; i < num_points; i++) {
853 854
    bezier->bezier.points[i].type = BEZ_CURVE_TO;
    bezier->bezier.corner_types[i] = BEZ_CORNER_SYMMETRIC;
855 856 857
  }

  new_handles_and_connections(bezier, num_points);
858

859 860
  /* The points might not be assigned at this point,
   * so don't try to use them */
861
  /*  beziershape_update_data(bezier);*/
862 863
}

864 865 866 867
/** Copy a beziershape objects.  This function in turn invokes object_copy.
 * @param from The object to copy from.
 * @param to The object to copy to.
 */
868
void
869
beziershape_copy (BezierShape *from, BezierShape *to)
870 871
{
  int i;
872
  DiaObject *toobj, *fromobj;
873 874 875 876 877 878

  toobj = &to->object;
  fromobj = &from->object;

  object_copy(fromobj, toobj);

879
  beziercommon_copy (&from->bezier, &to->bezier);
880

881
  for (i = 0; i < toobj->num_handles; i++) {
882
    toobj->handles[i] = g_new0(Handle,1);
883
    setup_handle(toobj->handles[i], fromobj->handles[i]->id);
884
  }
885 886 887 888
  for (i = 0; i < toobj->num_connections; i++) {
    toobj->connections[i] = g_new0(ConnectionPoint, 1);
    toobj->connections[i]->object = &to->object;
    toobj->connections[i]->flags = fromobj->connections[i]->flags;
889
  }
890

891
  memcpy(&to->extra_spacing,&from->extra_spacing,sizeof(to->extra_spacing));  
892 893 894 895
  beziershape_update_data(to);
}

void
896
beziershape_destroy (BezierShape *bezier)
897
{
898
  int i, nh;
899
  Handle **temp_handles;
900
  ConnectionPoint **temp_cps;
901 902 903

  /* Need to store these temporary since object.handles is
     freed by object_destroy() */
904 905 906
  nh = bezier->object.num_handles;
  temp_handles = g_new(Handle *, nh);
  for (i = 0; i < nh; i++)
907 908
    temp_handles[i] = bezier->object.handles[i];

909 910 911 912
  temp_cps = g_new(ConnectionPoint *, bezier->object.num_connections);
  for (i = 0; i < bezier->object.num_connections; i++)
    temp_cps[i] = bezier->object.connections[i];
  
913 914
  object_destroy(&bezier->object);

915
  for (i = 0; i < nh; i++)
916 917
    g_free(temp_handles[i]);
  g_free(temp_handles);
918 919 920 921

  for (i = 0; i < bezier->object.num_connections; i++)
    g_free(temp_cps[i]);
  g_free(temp_cps);
922
  
923 924
  g_free(bezier->bezier.points);
  g_free(bezier->bezier.corner_types);
925 926 927
}


928 929 930
/** Save the data defined by a beziershape object to XML.
 * @param bezier The object to save.
 * @param obj_node The XML node to save it into
931
 * @param ctx The context to transport error information.
932
 */
933
void
934
beziershape_save (BezierShape *bezier,
935 936
		  ObjectNode obj_node,
		  DiaContext *ctx)
937 938 939 940
{
  int i;
  AttributeNode attr;

941
  object_save(&bezier->object, obj_node, ctx);
942 943 944

  attr = new_attribute(obj_node, "bez_points");

945
  data_add_point(attr, &bezier->bezier.points[0].p1, ctx);
946 947
  for (i = 1; i < bezier->bezier.num_points; i++) {
    if (BEZ_MOVE_TO == bezier->bezier.points[i].type)
948
      g_warning("only first BezPoint can be a BEZ_MOVE_TO");
949 950
    data_add_point(attr, &bezier->bezier.points[i].p1, ctx);
    data_add_point(attr, &bezier->bezier.points[i].p2, ctx);
951
    if (i < bezier->bezier.num_points - 1)
952
      data_add_point(attr, &bezier->bezier.points[i].p3, ctx);
953 954 955
  }

  attr = new_attribute(obj_node, "corner_types");
956
  for (i = 0; i < bezier->bezier.num_points; i++)
957
    data_add_enum(attr, bezier->bezier.corner_types[i], ctx);
958 959
}

960 961 962 963 964 965
/** Load a beziershape object from XML.
 * Does object_init() on the bezierconn object.
 * @param bezier A newly allocated bezierconn object to load into.
 * @param obj_node The XML node to load from.
 * @param ctx The context in which this function is called
 */
966
void
967 968 969
beziershape_load (BezierShape *bezier,
		  ObjectNode obj_node,
		  DiaContext *ctx)
970 971 972 973 974
{
  int i;
  AttributeNode attr;
  DataNode data;
  
975
  DiaObject *obj = &bezier->object;
976

977
  object_load(obj, obj_node, ctx);
978 979 980 981

  attr = object_find_attribute(obj_node, "bez_points");

  if (attr != NULL)
982
    bezier->bezier.num_points = attribute_num_data(attr) / 3 + 1;
983
  else
984
    bezier->bezier.num_points = 0;
985

986 987
  object_init(obj, 3 * (bezier->bezier.num_points - 1),
	      2 * (bezier->bezier.num_points - 1) + 1);
988 989

  data = attribute_first_data(attr);
990 991 992 993 994
  if (bezier->bezier.num_points != 0) {
    bezier->bezier.points = g_new(BezPoint, bezier->bezier.num_points);
    bezier->bezier.points[0].type = BEZ_MOVE_TO;
    data_point(data, &bezier->bezier.points[0].p1, ctx);
    bezier->bezier.points[0].p3 = bezier->bezier.points[0].p1;
995 996
    data = data_next(data);

997 998 999
    for (i = 1; i < bezier->bezier.num_points; i++) {
      bezier->bezier.points[i].type = BEZ_CURVE_TO;
      data_point(data, &bezier->bezier.points[i].p1, ctx);
1000
      data = data_next(data);
1001
      data_point(data, &bezier->bezier.points[i].p2, ctx);
1002
      data = data_next(data);
1003 1004
      if (i < bezier->bezier.num_points - 1) {
	data_point(data, &bezier->bezier.points[i].p3, ctx);
1005 1006
	data = data_next(data);
      } else
1007
	bezier->bezier.points[i].p3 = bezier->bezier.points[0].p1;
1008 1009 1010
    }
  }

1011
  bezier->bezier.corner_types = g_new(BezCornerType, bezier->bezier.num_points);
1012
  attr = object_find_attribute(obj_node, "corner_types");
1013
  /* if corner_types is missing or corrupt */
1014 1015 1016
  if (!attr || attribute_num_data(attr) != bezier->bezier.num_points) {
    for (i = 0; i < bezier->bezier.num_points; i++)
      bezier->bezier.corner_types[i] = BEZ_CORNER_SYMMETRIC;
1017 1018
  } else {
    data = attribute_first_data(attr);
1019 1020
    for (i = 0; i < bezier->bezier.num_points; i++) {
      bezier->bezier.corner_types[i] = data_enum(data, ctx);
1021 1022 1023 1024
      data = data_next(data);
    }
  }

1025
  for (i = 0; i < bezier->bezier.num_points - 1; i++) {
1026 1027 1028
    obj->handles[3*i] = g_new0(Handle,1);
    obj->handles[3*i+1] = g_new0(Handle,1);
    obj->handles[3*i+2]   = g_new0(Handle,1);
1029 1030 1031 1032

    setup_handle(obj->handles[3*i], HANDLE_RIGHTCTRL);
    setup_handle(obj->handles[3*i+1], HANDLE_LEFTCTRL);
    setup_handle(obj->handles[3*i+2],   HANDLE_BEZMAJOR);
1033
  }
1034
  for (i = 0; i < obj->num_connections; i++) {
1035 1036 1037
    obj->connections[i] = g_new0(ConnectionPoint, 1);
    obj->connections[i]->object = obj;
  }
1038
  obj->connections[obj->num_connections-1]->flags = CP_FLAGS_MAIN;
1039 1040 1041 1042

  beziershape_update_data(bezier);
}

1043 1044 1045 1046 1047
/*** Undo support ***/

/** Free undo information about adding or removing points.
 * @param change The undo information to free.
 */
1048
static void
1049
beziershape_point_change_free (struct BezPointChange *change)
1050 1051 1052 1053 1054 1055
{
  if ( (change->type==TYPE_ADD_POINT && !change->applied) ||
       (change->type==TYPE_REMOVE_POINT && change->applied) ){
    g_free(change->handle1);
    g_free(change->handle2);
    g_free(change->handle3);
1056 1057
    g_free(change->cp1);
    g_free(change->cp2);
1058 1059 1060
    change->handle1 = NULL;
    change->handle2 = NULL;
    change->handle3 = NULL;
1061 1062
    change->cp1 = NULL;
    change->cp2 = NULL;
1063 1064 1065
  }
}

1066 1067 1068 1069
/** Apply a point addition/removal.
 * @param change The change to apply.
 * @param obj The object (must be a BezierShape) to apply the change to.
 */
1070
static void
1071
beziershape_point_change_apply (struct BezPointChange *change, DiaObject *obj)
1072 1073 1074 1075 1076 1077
{
  change->applied = 1;
  switch (change->type) {
  case TYPE_ADD_POINT:
    add_handles((BezierShape *)obj, change->pos, &change->point,
		change->corner_type,
1078 1079
		change->handle1, change->handle2, change->handle3,
		change->cp1, change->cp2);
1080 1081 1082 1083 1084 1085 1086 1087 1088 1089
    break;
  case TYPE_REMOVE_POINT:
    object_unconnect(obj, change->handle1);
    object_unconnect(obj, change->handle2);
    object_unconnect(obj, change->handle3);
    remove_handles((BezierShape *)obj, change->pos);
    break;
  }
}

1090 1091 1092 1093
/** Revert (unapply) a point addition/removal.
 * @param change The change to revert.
 * @param obj The object (must be a BezierShape) to revert the change of.
 */
1094
static void
1095
beziershape_point_change_revert (struct BezPointChange *change, DiaObject *obj)
1096 1097 1098 1099 1100 1101 1102 1103
{
  switch (change->type) {
  case TYPE_ADD_POINT:
    remove_handles((BezierShape *)obj, change->pos);
    break;
  case TYPE_REMOVE_POINT:
    add_handles((BezierShape *)obj, change->pos, &change->point,
		change->corner_type,
1104 1105
		change->handle1, change->handle2, change->handle3,
		change->cp1, change->cp2);
1106 1107 1108 1109 1110 1111
    break;
  }
  change->applied = 0;
}

static ObjectChange *
1112 1113 1114 1115 1116 1117 1118 1119 1120 1121
beziershape_create_point_change (BezierShape *bezier,
				 enum change_type type,
				 BezPoint *point,
				 BezCornerType corner_type,
				 int pos,
				 Handle *handle1,
				 Handle *handle2,
				 Handle *handle3,
				 ConnectionPoint *cp1,
				 ConnectionPoint *cp2)
1122
{
1123
  struct BezPointChange *change;
1124

1125
  change = g_new(struct BezPointChange, 1);
1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141

  change->obj_change.apply =
    (ObjectChangeApplyFunc)beziershape_point_change_apply;
  change->obj_change.revert =
    (ObjectChangeRevertFunc)beziershape_point_change_revert;
  change->obj_change.free =
    (ObjectChangeFreeFunc)beziershape_point_change_free;

  change->type = type;
  change->applied = 1;
  change->point = *point;
  change->corner_type = corner_type;
  change->pos = pos;
  change->handle1 = handle1;
  change->handle2 = handle2;
  change->handle3 = handle3;
1142 1143
  change->cp1 = cp1;
  change->cp2 = cp2;
1144 1145 1146 1147

  return (ObjectChange *)change;
}

1148 1149 1150 1151 1152
/** Apply a change of corner type.  This may change the position of the
 * control handles by calling beziershape_straighten_corner.
 * @param change The undo information to apply.
 * @param obj The object to apply the undo information too.
 */
1153
static void
1154 1155
beziershape_corner_change_apply (struct CornerChange *change,
				 DiaObject *obj)
1156
{
1157 1158
  BezierShape *bezier = (BezierShape *)obj;
  int handle_nr = get_handle_nr(bezier, change->handle);
1159 1160
  int comp_nr = get_major_nr(handle_nr);

1161
  beziershape_straighten_corner(bezier, comp_nr);
1162

1163
  bezier->bezier.corner_types[comp_nr] = change->new_type;
1164
  if (comp_nr == 0)
1165 1166 1167
    bezier->bezier.corner_types[bezier->bezier.num_points-1] = change->new_type;
  if (comp_nr == bezier->bezier.num_points - 1)
    bezier->bezier.corner_types[0] = change->new_type;
1168 1169 1170 1171

  change->applied = 1;
}

1172 1173 1174 1175 1176
/** Revert (unapply) a change of corner type.  This may move the position
 * of the control handles to what they were before applying.
 * @param change Undo information to apply.
 * @param obj The beziershape object to apply the change to.
 */
1177
static void
1178 1179
beziershape_corner_change_revert (struct CornerChange *change,
				  DiaObject *obj)
1180
{
1181 1182
  BezierShape *bezier = (BezierShape *)obj;
  int handle_nr = get_handle_nr(bezier, change->handle);
1183 1184
  int comp_nr = get_major_nr(handle_nr);

1185 1186 1187
  bezier->bezier.points[comp_nr].p2 = change->point_left;
  if (comp_nr == bezier->bezier.num_points - 1)
    bezier->bezier.points[1].p1 = change->point_right;
1188
  else
1189 1190
    bezier->bezier.points[comp_nr+1].p1 = change->point_right;
  bezier->bezier.corner_types[comp_nr] = change->old_type;  
1191
  if (comp_nr == 0)
1192 1193 1194
    bezier->bezier.corner_types[bezier->bezier.num_points-1] = change->new_type;
  if (comp_nr == bezier->bezier.num_points - 1)
    bezier->bezier.corner_types[0] = change->new_type;
1195 1196 1197 1198

  change->applied = 0;
}

1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209
/** Create new undo information about a changing the type of a corner.
 * Note that the created ObjectChange object has nothing in it that needs
 * freeing.
 * @param bezier The bezier object this applies to.
 * @param handle The handle of the corner being changed.
 * @param point_left The position of the left control handle.
 * @param point_right The position of the right control handle.
 * @param old_corner_type The corner type before applying.
 * @param new_corner_type The corner type being changed to.
 * @returns Newly allocated undo information.
 */
1210
static ObjectChange *
1211
beziershape_create_corner_change (BezierShape *bezier,
1212 1213 1214 1215 1216
				  Handle *handle,
				  Point *point_left,
				  Point *point_right,
				  BezCornerType old_corner_type,
				  BezCornerType new_corner_type)
1217 1218 1219 1220 1221 1222
{
  struct CornerChange *change;

  change = g_new(struct CornerChange, 1);

  change->obj_change.apply =
1223
	(ObjectChangeApplyFunc)beziershape_corner_change_apply;
1224
  change->obj_change.revert =
1225 1226 1227
	(ObjectChangeRevertFunc)beziershape_corner_change_revert;
  change->obj_change.free =
	(ObjectChangeFreeFunc)NULL;
1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238

  change->old_type =