@@ -76,8 +76,6 @@ public class CanvasItemBubble : CanvasItem {
7676 _sel_cursors[6 ] = new Cursor .from_name( " s-resize" , null );
7777 _sel_cursors[7 ] = new Cursor .from_name( " w-resize" , null );
7878 _sel_cursors[8 ] = new Cursor .from_name( " crosshair" , null ); // Talking point
79- _sel_cursors[9 ] = new Cursor .from_name( " ew-resize" , null ); // Base 0
80- _sel_cursors[10 ] = new Cursor .from_name( " ew-resize" , null ); // Base 1
8179 }
8280
8381 /* Create the points */
@@ -91,8 +89,6 @@ public class CanvasItemBubble : CanvasItem {
9189 points. append_val( new CanvasPoint ( CanvasPointType . RESIZER2 ) ); // bottom
9290 points. append_val( new CanvasPoint ( CanvasPointType . RESIZER3 ) ); // left
9391 points. append_val( new CanvasPoint ( CanvasPointType . CONTROL ) ); // Talk point
94- points. append_val( new CanvasPoint ( CanvasPointType . CONTROL ) ); // Where talk point attaches to bubble
95- points. append_val( new CanvasPoint ( CanvasPointType . CONTROL ) ); // Where talk point attaches to bubble
9692 }
9793
9894 public override void copy ( CanvasItem item ) {
@@ -129,22 +125,6 @@ public class CanvasItemBubble : CanvasItem {
129125 points. index( 8 ). copy_coords( bbox. mid_x(), (bbox. y2() + 70 ) );
130126 }
131127
132- if ( ! _base_moved ) {
133- points. index( 9 ). copy_coords( (bbox. mid_x() + 20 ), bbox. y2() );
134- points. index( 10 ). copy_coords( (bbox. mid_x() + 50 ), bbox. y2() );
135- } else {
136- points. index( 9 ). copy_coords( points. index( 9 ). x, bbox. y2() );
137- points. index( 10 ). copy_coords( points. index( 10 ). x, bbox. y2() );
138- }
139-
140- if ( points. index( 10 ). x > bbox. x2() ) {
141- points. index( 10 ). x = bbox. x2();
142- }
143-
144- if ( points. index( 9 ). x < bbox. x1() ) {
145- points. index( 9 ). x = bbox. x1();
146- }
147-
148128 }
149129
150130 /* Adjusts the bounding box */
@@ -164,8 +144,6 @@ public class CanvasItemBubble : CanvasItem {
164144 case 6 : box. height + = diffy; break ;
165145 case 7 : box. x + = diffx; box. width - = diffx; break ;
166146 case 8 : points. index( 8 ). x + = diffx; points. index( 8 ). y + = diffy; _point_moved = true ; break ;
167- case 9 : points. index( 9 ). x + = diffx; _base_moved = true ; break ;
168- case 10 : points. index( 10 ). x + = diffx; _base_moved = true ; break ;
169147 }
170148
171149 if ( (box. width >= 60 ) && (box. height >= 1 ) ) {
@@ -179,19 +157,264 @@ public class CanvasItemBubble : CanvasItem {
179157 return ( _sel_cursors[index] );
180158 }
181159
160+ /* Helper function that finds the two tangential points on a circle to a given point */
161+ private bool find_tangents ( CanvasPoint center , double radius , CanvasPoint external , CanvasPoint pt1 , CanvasPoint pt2 ) {
162+
163+ var dx = center. x - external. x;
164+ var dy = center. y - external. y;
165+ var dsq = (dx * dx) + (dy * dy);
166+ var rsq = radius * radius;
167+ if ( dsq < rsq ) {
168+ return ( false );
169+ }
170+ var l = Math . sqrt( dsq - rsq );
171+
172+ return ( find_circle_circle_intersections( center, radius, external, l, pt1, pt2 ) );
173+
174+ }
175+
176+ /* Helper function that finds the two points where two circles intersect */
177+ private bool find_circle_circle_intersections ( CanvasPoint c0 , double r0 , CanvasPoint c1 , double r1 , CanvasPoint pt1 , CanvasPoint pt2 ) {
178+
179+ var dx = c0. x - c1. x;
180+ var dy = c0. y - c1. y;
181+ var dist = Math . sqrt( (dx * dx) + (dy * dy) );
182+ if ( (dist > (r0 + r1)) || (dist < Math . fabs( r0 - r1 )) || ((dist == 0 ) && (r0 == r1)) ) {
183+ return ( false );
184+ }
185+ var a = ((r0 * r0) - (r1 * r1) + (dist * dist)) / (2 * dist);
186+ var h = Math . sqrt( (r0 * r0) - (a * a) );
187+ var cx2 = c0. x + a * (c1. x - c0. x) / dist;
188+ var cy2 = c0. y + a * (c1. y - c0. y) / dist;
189+
190+ pt1. copy_coords( (cx2 + (h * (c1. y - c0. y) / dist)), (cy2 - (h * (c1. x - c0. x) / dist)) );
191+ pt2. copy_coords( (cx2 - (h * (c1. y - c0. y) / dist)), (cy2 + (h * (c1. x - c0. x) / dist)) );
192+
193+ return ( true );
194+
195+ }
196+
197+ // -------------------------------------------------------------
198+ // Returns the X, Y point where two lines intersect where p0 and p1
199+ // are the points for the first line and p2, p3 are the points for the second line
200+ private bool get_line_intersection ( CanvasPoint p0 , CanvasPoint p1 , CanvasPoint p2 , CanvasPoint p3 , out CanvasPoint ? pi ) {
201+
202+ var s1 = new CanvasPoint .from_coords( (p1. x - p0. x), (p1. y - p0. y) );
203+ var s2 = new CanvasPoint .from_coords( (p3. x - p2. x), (p3. y - p2. y) );
204+
205+ var s = (- s1. y * (p0. x - p2. x) + s1. x * (p0. y - p2. y)) / (- s2. x * s1. y + s1. x * s2. y);
206+ var t = ( s2. x * (p0. y - p2. y) - s2. y * (p0. x - p2. x)) / (- s2. x * s1. y + s1. x * s2. y);
207+
208+ if ( (s >= 0 ) && (s <= 1 ) && (t >= 0 ) && (t <= 1 ) ) {
209+ pi = new CanvasPoint .from_coords( (p0. x + (t * s1. x)), (p0. y + (t * s1. y)) );
210+ return ( true );
211+ }
212+
213+ pi = null ;
214+ return ( false ); // No collision
215+
216+ }
217+
218+ // -------------------------------------------------------------
219+ // Draw the bubble starting at the top.
220+ private bool draw_bubble_top ( Context ctx , CanvasPoint ? pa , CanvasPoint ? pb ) {
221+
222+ var pa_n = (pa != null ) && (pa. y == bbox. y1());
223+ var pb_n = (pb != null ) && (pb. y == bbox. y1());
224+
225+ if ( ! pa_n && ! pb_n ) return ( false );
226+
227+ var deg = Math . PI / 180.0 ;
228+ var pa_e = (pa. x == bbox. x2());
229+ var pa_w = (pa. x == bbox. x1());
230+ var pb_e = (pb. x == bbox. x2());
231+ var pb_w = (pb. x == bbox. x1());
232+
233+ CanvasPoint p1, p2;
234+ bool ul, ur;
235+
236+ if ( pa_w || pb_e || (pa. x < pb. x) ) {
237+ p1 = pa; p2 = pb; ul = pa_w; ur = pb_e;
238+ } else {
239+ p1 = pb; p2 = pa; ul = pb_w; ur = pa_e;
240+ }
241+
242+ if ( ul ) {
243+ ctx. move_to( p1. x, p1. y );
244+ ctx. line_to( points. index( 8 ). x, points. index( 8 ). y );
245+ ctx. line_to( p2. x, p2. y );
246+ } else {
247+ var r = ((p1. x - bbox. x1()) < _radius) ? (p1. x - bbox. x1()) : _radius;
248+ ctx. arc( (bbox. x1() + r), (bbox. y1() + r), r, (180 * deg), (270 * deg) );
249+ ctx. line_to( p1. x, p1. y );
250+ ctx. line_to( points. index( 8 ). x, points. index( 8 ). y );
251+ ctx. line_to( p2. x, p2. y );
252+ }
253+ if ( ! ur ) {
254+ var r = ((bbox. x2() - p2. x) < _radius) ? (bbox. x2() - p2. x) : _radius;
255+ ctx. arc( (bbox. x2() - r), (bbox. y1() + r), r, (- 90 * deg), (0 * deg) );
256+ }
257+
258+ ctx. arc( (bbox. x2() - _radius), (bbox. y2() - _radius), _radius, (0 * deg), (90 * deg) );
259+ ctx. arc( (bbox. x1() + _radius), (bbox. y2() - _radius), _radius, (90 * deg), (180 * deg) );
260+
261+ return ( true );
262+
263+ }
264+
265+ // -------------------------------------------------------------
266+ // Draw the bubble starting at the bottom.
267+ private bool draw_bubble_bottom ( Context ctx , CanvasPoint ? pa , CanvasPoint ? pb ) {
268+
269+ var pa_s = (pa != null ) && (pa. y == bbox. y2());
270+ var pb_s = (pb != null ) && (pb. y == bbox. y2());
271+
272+ if ( ! pa_s && ! pb_s ) return ( false );
273+
274+ var deg = Math . PI / 180.0 ;
275+ var pa_e = (pa. x == bbox. x2());
276+ var pa_w = (pa. x == bbox. x1());
277+ var pb_e = (pb. x == bbox. x2());
278+ var pb_w = (pb. x == bbox. x1());
279+
280+ CanvasPoint p1, p2;
281+ bool ll, lr;
282+
283+ if ( pa_e || pb_w || (pa. x > pb. x) ) {
284+ p1 = pa; p2 = pb; ll = pb_w; lr = pa_e;
285+ } else {
286+ p1 = pb; p2 = pa; ll = pa_w; lr = pb_e;
287+ }
288+
289+ if ( lr ) {
290+ ctx. move_to( p1. x, p1. y );
291+ ctx. line_to( points. index( 8 ). x, points. index( 8 ). y );
292+ ctx. line_to( p2. x, p2. y );
293+ } else {
294+ var r = ((bbox. x2() - p1. x) < _radius) ? (bbox. x2() - p1. x) : _radius;
295+ ctx. arc( (bbox. x2() - r), (bbox. y2() - r), r, (0 * deg), (90 * deg) );
296+ ctx. line_to( p1. x, p1. y );
297+ ctx. line_to( points. index( 8 ). x, points. index( 8 ). y );
298+ ctx. line_to( p2. x, p2. y );
299+ }
300+ if ( ! ll ) {
301+ var r = ((p2. x - bbox. x1()) < _radius) ? (p2. x - bbox. x1()) : _radius;
302+ ctx. arc( (bbox. x1() + r), (bbox. y2() - r), r, (90 * deg), (180 * deg) );
303+ }
304+
305+ ctx. arc( (bbox. x1() + _radius), (bbox. y1() + _radius), _radius, (180 * deg), (270 * deg) );
306+ ctx. arc( (bbox. x2() - _radius), (bbox. y1() + _radius), _radius, (- 90 * deg), (0 * deg) );
307+
308+ return ( true );
309+
310+ }
311+
312+
313+ // -------------------------------------------------------------
314+ // Draw the bubble starting on the right
315+ private bool draw_bubble_right ( Context ctx , CanvasPoint ? pa , CanvasPoint ? pb ) {
316+
317+ var pa_e = (pa != null ) && (pa. x == bbox. x2());
318+ var pb_e = (pb != null ) && (pb. x == bbox. x2());
319+
320+ if ( ! pa_e && ! pb_e ) return ( false );
321+
322+ var deg = Math . PI / 180.0 ;
323+ CanvasPoint p1, p2;
324+
325+ if ( pa. y < pb. y ) {
326+ p1 = pa; p2 = pb;
327+ } else {
328+ p1 = pb; p2 = pa;
329+ }
330+
331+ var r1 = ((p1. y - bbox. y1()) < _radius) ? (p1. y - bbox. y1()) : _radius;
332+ var r2 = ((bbox. y2() - p2. y) < _radius) ? (bbox. y2() - p2. y) : _radius;
333+ ctx. arc( (bbox. x2() - r1), (bbox. y1() + r1), r1, (- 90 * deg), (0 * deg) );
334+ ctx. line_to( p1. x, p1. y );
335+ ctx. line_to( points. index( 8 ). x, points. index( 8 ). y );
336+ ctx. line_to( p2. x, p2. y );
337+ ctx. arc( (bbox. x2() - r2), (bbox. y2() - r2), r2, (0 * deg), (90 * deg) );
338+ ctx. arc( (bbox. x1() + _radius), (bbox. y2() - _radius), _radius, (90 * deg), (180 * deg) );
339+ ctx. arc( (bbox. x1() + _radius), (bbox. y1() + _radius), _radius, (180 * deg), (270 * deg) );
340+
341+ return ( true );
342+
343+ }
344+
345+ // -------------------------------------------------------------
346+ // Draw the bubble starting on the left
347+ private bool draw_bubble_left ( Context ctx , CanvasPoint ? pa , CanvasPoint ? pb ) {
348+
349+ var pa_w = (pa != null ) && (pa. x == bbox. x1());
350+ var pb_w = (pb != null ) && (pb. x == bbox. x1());
351+
352+ if ( ! pa_w && ! pb_w ) return ( false );
353+
354+ var deg = Math . PI / 180.0 ;
355+ CanvasPoint p1, p2;
356+
357+ if ( pa. y > pb. y ) {
358+ p1 = pa; p2 = pb;
359+ } else {
360+ p1 = pb; p2 = pa;
361+ }
362+
363+ var r1 = ((bbox. y2() - p1. y) < _radius) ? (bbox. y2() - p1. y) : _radius;
364+ var r2 = (p2. y - (bbox. y1()) < _radius) ? (p2. y - bbox. y1()) : _radius;
365+ ctx. arc( (bbox. x1() + r1), (bbox. y2() - r1), r1, (90 * deg), (180 * deg) );
366+ ctx. line_to( p1. x, p1. y );
367+ ctx. line_to( points. index( 8 ). x, points. index( 8 ). y );
368+ ctx. line_to( p2. x, p2. y );
369+ ctx. arc( (bbox. x1() + r2), (bbox. y1() + r2), r2, (180 * deg), (270 * deg) );
370+ ctx. arc( (bbox. x2() - _radius), (bbox. y1() + _radius), _radius, (- 90 * deg), (0 * deg) );
371+ ctx. arc( (bbox. x2() - _radius), (bbox. y2() - _radius), _radius, (0 * deg), (90 * deg) );
372+
373+ return ( true );
374+
375+ }
376+
377+ // -------------------------------------------------------------
378+ // Draw a talking bubble.
182379 private void draw_bubble ( Context ctx ) {
183380
381+ var center = new CanvasPoint .from_coords( bbox. mid_x(), bbox. mid_y() );
184382 var deg = Math . PI / 180.0 ;
185- var radius = _radius;
383+ var rad = (bbox. width < bbox. height) ? (bbox. width / 5 ) : (bbox. height / 5 );
384+ var pt1 = new CanvasPoint ();
385+ var pt2 = new CanvasPoint ();
386+ var pa = new CanvasPoint (); // Intersecting point
387+ var pb = new CanvasPoint (); // Intersecting point
388+
389+ var rul = new CanvasPoint .from_coords( bbox. x1(), bbox. y1() );
390+ var rur = new CanvasPoint .from_coords( bbox. x2(), bbox. y1() );
391+ var rll = new CanvasPoint .from_coords( bbox. x1(), bbox. y2() );
392+ var rlr = new CanvasPoint .from_coords( bbox. x2(), bbox. y2() );
393+
394+ if ( ! find_tangents( center, rad, points. index( 8 ), pt1, pt2 ) ) return ;
395+
396+ if ( get_line_intersection( pt1, points. index( 8 ), rul, rur, out pa ) ||
397+ get_line_intersection( pt1, points. index( 8 ), rur, rlr, out pa ) ||
398+ get_line_intersection( pt1, points. index( 8 ), rlr, rll, out pa ) ||
399+ get_line_intersection( pt1, points. index( 8 ), rll, rul, out pa ) ) {
400+ if ( get_line_intersection( pt2, points. index( 8 ), rul, rur, out pb ) ||
401+ get_line_intersection( pt2, points. index( 8 ), rur, rlr, out pb ) ||
402+ get_line_intersection( pt2, points. index( 8 ), rlr, rll, out pb ) ||
403+ get_line_intersection( pt2, points. index( 8 ), rll, rul, out pb ) ) {
404+ ctx. new_sub_path();
405+ if ( draw_bubble_top( ctx, pa, pb ) || draw_bubble_bottom( ctx, pa, pb ) ||
406+ draw_bubble_left( ctx, pa, pb ) || draw_bubble_right( ctx, pa, pb ) ) {
407+ ctx. close_path();
408+ return ;
409+ }
410+ }
411+ }
186412
187413 ctx. new_sub_path();
188- ctx. arc( (bbox. x + bbox. width - radius), (bbox. y + radius), radius, (- 90 * deg), (0 * deg) );
189- ctx. arc( (bbox. x + bbox. width - radius), (bbox. y + bbox. height - radius), radius, (0 * deg), (90 * deg) );
190- ctx. line_to( points. index( 10 ). x, points. index( 10 ). y );
191- ctx. line_to( points. index( 8 ). x, points. index( 8 ). y );
192- ctx. line_to( points. index( 9 ). x, points. index( 9 ). y );
193- ctx. arc( (bbox. x + radius), (bbox. y + bbox. height - radius), radius, (90 * deg), (180 * deg) );
194- ctx. arc( (bbox. x + radius), (bbox. y + radius), radius, (180 * deg), (270 * deg) );
414+ ctx. arc( (bbox. x1() + _radius), (bbox. y1() + _radius), _radius, (180 * deg), (270 * deg) );
415+ ctx. arc( (bbox. x2() - _radius), (bbox. y1() + _radius), _radius, (- 90 * deg), (0 * deg) );
416+ ctx. arc( (bbox. x2() - _radius), (bbox. y2() - _radius), _radius, (0 * deg), (90 * deg) );
417+ ctx. arc( (bbox. x1() + _radius), (bbox. y2() - _radius), _radius, (90 * deg), (180 * deg) );
195418 ctx. close_path();
196419
197420 }
0 commit comments