6

On my application I need draw on TCanvas a "marker", like Google Maps marker (see the image).

google marker

I'd like use as parameters the radius, the height and the origin:

marker parameters

I don't have idea about algorithm to use. I can use an arc to draw the top, but how can I draw the bottom? Note: I need draw it both with GDI and GDI+ so any solution is welcome.

Martin
  • 1,065
  • 1
  • 17
  • 36
  • 2
    I don't want to be rude, but I don't think this got anything to do with Delphi or GDI in particular. It's more like looking for an algorithm that takes three parameters and outputs something like the above. – Günther the Beautiful Aug 20 '13 at 16:18
  • @GünthertheBeautiful it's possible but I need do it with Delphi using GDI and GDI+ – Martin Aug 20 '13 at 16:30
  • 1
    The point is that once you have the mathematical description, you can draw it in whatever language you fancy. It's categorically not a delphi question. FWIW I bet google use raster images. – David Heffernan Aug 20 '13 at 16:45
  • 1
    This has no 'inward curve' but it's a simple algoritm: http://www.how-to-draw-funny-cartoons.com/cartoon-water.html – Jan Doggen Aug 20 '13 at 17:00
  • 1
    @DavidHeffernan yes a mathematical description is OK for me. I have description what I need to do but a "simple" mathematical solution of the problem is a point of start, was I wrong to indicate delphi? Yes, Google use a raster image to solve the problem. – Martin Aug 20 '13 at 17:14
  • Why not use raster image? – David Heffernan Aug 20 '13 at 17:17
  • 1
    @DavidHeffernan bacause the user need change size and color dynamically – Martin Aug 20 '13 at 17:20

1 Answers1

10

Here is a quick example using a 200x200 PaintBox - it should at least give you an idea for the algorithm. I trust you can draw the black dot in the middle. Read up on Bezier Curves; PolyBezier defines cubic Bezier curves. (link)

bezier

Four points define a cubic Bezier curve - start, end, and two control points. Control points define the strength of curvature as the line moves from start to end.

var origin, innerL, midL, midR, lft, tp, rgt, innerR : TPoint;
    radius, hgt : integer;
begin    
  radius := 25;
  hgt := 90;    
  origin.X := 100;
  origin.Y := 180;
  //control points
  innerL.X := origin.X;
  innerL.Y := origin.Y - (hgt - radius) div 3;
  midL.X := origin.X - radius;
  midL.Y := origin.Y - 2*((hgt - radius) div 3);
  //top circle
  lft.X := origin.X - radius;
  lft.Y := origin.Y - (hgt - radius);
  tp.X := origin.X;
  tp.Y := origin.Y - hgt;
  rgt.X := origin.X + radius;
  rgt.Y := lft.Y;
  //control points
  midR.X := origin.X + radius;
  midR.Y := midL.Y;
  innerR.X := origin.X;
  innerR.Y := innerL.Y;

  PaintBox1.Canvas.Pen.Width := 2;
  PaintBox1.Canvas.PolyBezier([origin, innerL, midL, lft]);
  PaintBox1.Canvas.Arc(lft.X, tp.Y, rgt.X, rgt.Y + radius, rgt.X, rgt.Y, lft.X, lft.Y);
  PaintBox1.Canvas.PolyBezier([rgt, midR, innerR, origin]);
  //fill
  PaintBox1.Canvas.Brush.Color := clYellow;
  PaintBox1.Canvas.FloodFill(origin.X, origin.Y - radius,
                             Canvas.Pen.Color, TFillStyle.fsBorder);    
end;

To satisfy the point that you can do this with one bezier :

  // add four more control TPoints
  cornerL.X := lft.X;
  cornerL.Y := tp.Y + radius div 2;
  cL2.X := lft.X + radius div 2;
  cL2.Y := tp.Y;
  cR2.X := rgt.X - radius div 2;
  cR2.Y := tp.Y;
  cornerR.X := rgt.X;
  cornerR.Y := cornerL.Y;


  PaintBox1.Canvas.PolyBezier([origin, innerL, midL, lft,
                               cornerL, cL2, tp, cR2, cornerR, rgt,
                               midR, innerR, origin]);
J...
  • 30,968
  • 6
  • 66
  • 143
  • 4
    There's no need for ex-post `FloodFill` fill. Also, you can describe the whole marker shape by bezier curves (and draw it by a single `PolyBezier` call). – TLama Aug 20 '13 at 17:47
  • 2
    @TLama - yes, you can do it with one bezier. I did it quickly and didn't want to define four more control points - it was meant to be didactic, not production complete. Arc was faster to code, not necessarily better. Obviously a GDI solution would use paths (that define Beziers in the same way) and with paths the post fill is not needed, naturally. Again, it was a bezier how-to, not trying to do OP's job for him. – J... Aug 20 '13 at 18:29
  • 1
    Yes, that's better :-) And, you're right. With GDI+ you can make a path and fill the path. It's easier there. [+1] – TLama Aug 20 '13 at 19:02
  • 1
    @TLama Glad to have generated a smile :) – J... Aug 20 '13 at 19:54