00001 namespace Grendel.Base {
00002 using System;
00003 using System.Collections.Generic;
00004 using System.Diagnostics;
00005 using Grendel.Communication;
00006 using System.IO;
00007
00012 public interface ICanvasObject : IMessageReceiver {
00016 Canvas Canvas {get;}
00023 void AddToCommunicationGroup(string groupName);
00027 void RemoveFromCommunicationGroup(string groupName);
00031 IList<string> ComunnicationGroups {get;}
00032 }
00033
00039 public interface ICanvasCloneable {
00043 ICanvasObject Clone();
00044 }
00045
00053 public interface IEventListener {
00054 void MouseEventHandler(MouseButtons buttons, Position absolutePosition);
00055 }
00056
00062 [Flags]
00063 public enum MouseEventTarget {
00067 None = 0x0,
00074 OneInteractiveShape = 0x1,
00078 InteractiveShapes = 0x2,
00082 EventListeners = 0x4
00083 }
00084
00090 public class Canvas {
00091 private List<ICanvasObject> objs = new List<ICanvasObject>();
00092 private SortedList<int, List<IShape> > shapes = new SortedList<int,List<IShape> >();
00093 private List<IEventListener> listeners = new List<IEventListener>();
00094 private ICanvasImplementation impl;
00095 private int height;
00096 private int width;
00097 private double animationInterval = 0.0;
00098 private MouseEventTarget mouseEventTarget = MouseEventTarget.OneInteractiveShape;
00099 private int frame;
00100
00104 public int Width {
00105 get {
00106 return width;
00107 }
00108 }
00109
00113 public int Height {
00114 get {
00115 return height;
00116 }
00117 }
00118
00123 public double AnimationInterval {
00124 get {
00125 Debug.Assert(animationInterval > 0.0,
00126 "Animation interval is not yet set (before canvas displaying)");
00127 return animationInterval;
00128 }
00129
00130 }
00131
00132 internal IDialogBuilder DialogBuilder {
00133 get {
00134 return impl.DialogBuilder;
00135 }
00136 }
00137
00138
00142 public int Frame {
00143 get {
00144 return frame;
00145 }
00146 }
00147
00152 public MouseEventTarget MouseEventTarget {
00153 get {
00154 return mouseEventTarget;
00155 }
00156 set {
00157 mouseEventTarget = value;
00158 }
00159 }
00160
00161 internal Canvas(int width, int height, ICanvasImplementation implementation) {
00162 impl = implementation;
00163 implementation.Repainting += Repainting;
00164 this.width = width;
00165 this.height = height;
00166 Trace.Listeners.Add(new TextWriterTraceListener(Console.Error));
00167 }
00168
00173
00174 public void Remember(ICanvasObject newCanvasObject) {
00175 objs.Add(newCanvasObject);
00176 if(newCanvasObject is IShape) {
00177 IShape shape = (IShape)newCanvasObject;
00178 int layer = -shape.Layer;
00179 if(!shapes.ContainsKey(layer))
00180 shapes[layer] = new List<IShape>();
00181 shapes[layer].Add(shape);
00182 }
00183 impl.Invalidate();
00184 }
00185
00191 public void Forget(ICanvasObject fadingObject) {
00192 if(!objs.Contains(fadingObject))
00193 throw new ArgumentException("Fading object is not remembered by canvas");
00194 objs.Remove(fadingObject);
00195 if(fadingObject is IShape) {
00196 IShape shape = (IShape)fadingObject;
00197 int layer = shape.Layer;
00198 shapes[layer].Remove(shape);
00199 }
00200 Trace.Write(string.Format("Object of class {0} have been forgotten", fadingObject.GetType().Name));
00201 impl.Invalidate();
00202 }
00203
00213 public void Display(double animationInterval) {
00214
00215 this.animationInterval = animationInterval;
00216 impl.MouseInteraction += MouseEventDispatcher;
00217 impl.Animation += Animation;
00218 frame = 0;
00219 impl.Display(width, height,(int)(animationInterval*1000));
00220 }
00221
00228 public void Display() {
00229 Display(0.1);
00230 }
00231
00232 protected virtual void Repainting() {
00233 foreach(int layer in shapes.Keys) {
00234 foreach(IShape shape in shapes[layer])
00235 shape.Painting();
00236 }
00237 }
00238
00243 public IBitmap LoadBitmap(Stream bitmapStream) {
00244 return impl.LoadBitmap(bitmapStream);
00245 }
00246
00247 internal void Invalidate() {
00248 impl.Invalidate();
00249 }
00250
00251
00252 internal void MoveToLayer(IShape shape, int oldLayer, int newLayer) {
00253 shapes[oldLayer].Remove(shape);
00254 if(!shapes.ContainsKey(newLayer))
00255 shapes[newLayer] = new List<IShape>();
00256 shapes[newLayer].Add(shape);
00257 Invalidate();
00258 }
00259
00263 public double TextHeight(string text, Font font) {
00264 return impl.TextHeight(font, text);
00265 }
00266
00270 public double TextWidth(string text, Font f) {
00271 return impl.TextWidth(f, text);
00272 }
00273
00279 public void DrawLine(IShape shape, Position startPointLocation,
00280 Position endPointLocation, double lineWidth, Color color)
00281 {
00282 WorldMatrix m = shape.TotalTransformation;
00283 Position p1 = startPointLocation;
00284 Position p2 = endPointLocation;
00285 impl.DrawLine(m, (float)p1.X, (float)p1.Y, (float)p2.X, (float)p2.Y, (float)lineWidth, color);
00286 }
00287
00292 public void DrawLine(IShape shape, Position startPointLocation,
00293 Position endPointLocation, Color color) {
00294 DrawLine(shape, startPointLocation, endPointLocation, 1, color);
00295 }
00296
00302 public void DrawCircle(IShape shape, Position centerLocation, double r, double lineWidth, Color color) {
00303 WorldMatrix m = shape.TotalTransformation;
00304 Position c = centerLocation;
00305 impl.DrawCircle(m, (float)c.X, (float)c.Y, (float)r, (float)lineWidth, color);
00306 }
00307
00315 public void DrawText(IShape shape, Position topLeftLocation, string text,
00316 Font font, Color color) {
00317 WorldMatrix m = shape.TotalTransformation;
00318 Position p = topLeftLocation;
00319 impl.DrawText(m, (float)p.X, (float)p.Y, text, color, font);
00320 }
00321
00323
00328
00329 public void DrawText(IShape shape, Position topLeftLocation, string text) {
00330 DrawText(shape, topLeftLocation, text, Font.SansSerif, Color.Black);
00331 }
00332
00339 public void DrawRectangle(IShape shape, Position topLeftLocation,
00340 Position bottomRightLocation, double width, Color color) {
00341 WorldMatrix m = shape.TotalTransformation;
00342 Position l = topLeftLocation;
00343 Position r = bottomRightLocation;
00344 impl.DrawLine(m, (float)l.X, (float)l.Y, (float)r.X, (float)l.Y, (float) width, color);
00345 impl.DrawLine(m, (float)l.X, (float)l.Y, (float)l.X, (float)r.Y, (float) width, color);
00346 impl.DrawLine(m, (float)r.X, (float)r.Y, (float)l.X, (float)r.Y, (float) width, color);
00347 impl.DrawLine(m, (float)r.X, (float)r.Y, (float)r.X, (float)l.Y, (float) width, color);
00348 }
00349
00355 public void DrawRectangle(IShape shape, Position topLeftLocation,
00356 Position bottomRigthLocation, Color color) {
00357 DrawRectangle(shape, topLeftLocation, bottomRigthLocation, 1.0, color);
00358 }
00359
00365 public void DrawFilledTriangle(IShape shape, Position p1Location,
00366 Position p2Location, Position p3Location, Color color) {
00367 WorldMatrix m = shape.TotalTransformation;
00368 Position p1 = p1Location;
00369 Position p2 = p2Location;
00370 Position p3 = p3Location;
00371 impl.FillTriangle(m, (float)p1.X, (float)p1.Y, (float)p2.X, (float)p2.Y,
00372 (float)p3.X, (float)p3.Y, color, true);
00373 }
00374
00380 public void DrawFilledTriangle(IShape shape, Position[] cornerLocations, Color color) {
00381 Debug.Assert(cornerLocations.Length == 3, "Triangle must contains 3 corners");
00382 DrawFilledTriangle(shape, cornerLocations[0], cornerLocations[1],
00383 cornerLocations[3], color);
00384 }
00385
00392 public void DrawBitmap(IShape shape, Position topLeftLocation, IBitmap bitmap) {
00393 WorldMatrix m = shape.TotalTransformation;
00394 Position p = topLeftLocation;
00395 impl.DrawBitmap(m, (float)p.X, (float)p.Y, bitmap);
00396 }
00397
00398
00404 public void DrawBezierCurve(IShape shape, Position p0, Position p1, Position p2,
00405 Position p3, double widthOfCurve, Color color) {
00406 WorldMatrix m = shape.TotalTransformation;
00407 impl.DrawBezier(m, (float)p0.X, (float)p0.Y,
00408 (float)p1.X, (float)p1.Y,
00409 (float)p2.X, (float)p2.Y,
00410 (float)p3.X, (float)p3.Y, (float)widthOfCurve, color);
00411 }
00412
00417 public IEnumerator<ICanvasObject> CanvasObjects {
00418 get {
00419 return (new List<ICanvasObject>(objs)).GetEnumerator();
00420 }
00421 }
00422
00427 public IEnumerator<T> ObjectsOfType<T>() where T : ICanvasObject {
00428 foreach(ICanvasObject obj in new List<ICanvasObject>(objs))
00429 if(obj is T)
00430 yield return (T)obj;
00431 }
00432
00437 public IEnumerator<IShape> Shapes {
00438 get {
00439 return ObjectsOfType<IShape>();
00440 }
00441 }
00442
00443 private delegate void InteractionDelegate();
00444
00445 private List<InteractionDelegate> FindOptimalShape(MouseButtons buttons,
00446 Position absolutePosition) {
00447 List<InteractionDelegate> result = new List<InteractionDelegate>();
00448 double min = double.MaxValue;
00449 IInteractiveShape optTarget = null;
00450 Position optClickLocation = Position.Origin;
00451 foreach(int layer in shapes.Keys) {
00452 foreach(IShape shape in shapes[layer]) {
00453 if(shape is IInteractiveShape) {
00454 IInteractiveShape target = (IInteractiveShape) shape;
00455 if (!target.IsActive)
00456 continue;
00457 Position clickLocation =
00458 target.TotalTransformation.InvertedMatrix.Transform(absolutePosition);
00459 if(clickLocation.IsInside(target.LeftTopCorner, target.RightBottomCorner)) {
00460 double dist = Position.Origin.Distance(clickLocation);
00461 if(dist < min) {
00462 min = dist;
00463 optTarget = target;
00464 optClickLocation = clickLocation;
00465 }
00466 }
00467 }
00468 }
00469 if(optTarget != null) {
00470 result.Add( delegate () {optTarget.Interaction(buttons, optClickLocation);});
00471 return result;
00472 }
00473 }
00474 return result;
00475 }
00476
00477 private List<InteractionDelegate> FindShapes(MouseButtons buttons, Position absolutePosition) {
00478 List<InteractionDelegate> result = new List<InteractionDelegate>();
00479 foreach(int layer in shapes.Keys) {
00480 foreach(IShape shape in shapes[layer]) {
00481 if(shape is IInteractiveShape) {
00482 IInteractiveShape target = (IInteractiveShape) shape;
00483 if (!target.IsActive)
00484 continue;
00485 Position clickLocation =
00486 target.TotalTransformation.InvertedMatrix.Transform(absolutePosition);
00487 if(clickLocation.IsInside(target.LeftTopCorner, target.RightBottomCorner)) {
00488 result.Add(delegate(){target.Interaction(buttons, clickLocation);});
00489 }
00490 }
00491 }
00492 }
00493 return result;
00494 }
00495
00496 protected virtual void MouseEventDispatcher(MouseButtons buttons,
00497 Position absolutePosition) {
00498 MouseEventTarget savedTarget = mouseEventTarget;
00499 List<InteractionDelegate> interactions = new List<InteractionDelegate>();
00500 if((savedTarget & MouseEventTarget.OneInteractiveShape) != 0)
00501 interactions.AddRange(FindOptimalShape(buttons, absolutePosition));
00502 if((savedTarget & MouseEventTarget.InteractiveShapes) != 0)
00503 interactions.AddRange(FindShapes(buttons, absolutePosition));
00504
00505 foreach(InteractionDelegate interaction in interactions) {
00506 interaction();
00507 }
00508
00509 if((savedTarget & MouseEventTarget.EventListeners) != 0) {
00510 foreach(IEventListener listener in listeners.ToArray()) {
00511 listener.MouseEventHandler(buttons, absolutePosition);
00512 }
00513 }
00514 }
00515
00516 protected virtual void Animation() {
00517 bool repaint = false;
00518
00519 foreach(ICanvasObject o in objs.ToArray()){
00520 if(o is IDynamicObject)
00521 repaint = ((IDynamicObject)o).AnimationStep(frame) || repaint;
00522 }
00523 if(repaint)
00524 impl.Invalidate();
00525 frame++;
00526 }
00527
00531 public void AddEventListener(IEventListener listener) {
00532 listeners.Add(listener);
00533 }
00534
00541 public void RemoveEventListener(IEventListener listener) {
00542 listeners.Remove(listener);
00543 }
00544 }
00545
00549 public class CanvasFactory {
00550 private int width;
00551 private int height;
00552
00553 private static Dictionary<string, ICanvasImplementation> imap;
00554
00555 static CanvasFactory() {
00556 imap = new Dictionary<string, ICanvasImplementation>();
00557 imap["WinForms"] = new WFormsCanvasImplementation();
00558 }
00559
00563 public CanvasFactory(int width, int height) {
00564 Debug.Assert(width >= 100 && width <= 10000);
00565 Debug.Assert(height >= 100 && height <= 10000);
00566 this.width = width;
00567 this.height = height;
00568 }
00569
00573 public CanvasFactory() : this(500, 500) {}
00574
00578 public Canvas BestCanvas {
00579 get {return CanvasByFramework("WinForms");}
00580 }
00581
00585 public Canvas StandardCanvas {
00586 get {return CanvasByFramework("WinForms");}
00587 }
00588
00593 public Canvas CanvasByFramework(string frameworkName) {
00594 if (!imap.ContainsKey(frameworkName)) {
00595 string[] keys = new string[imap.Count];
00596 imap.Keys.CopyTo(keys, 0);
00597 string supported = string.Join(",", keys);
00598 throw new NotSupportedException(
00599 string.Format("This framework is not supported. Supported frameworks: {0}",
00600 supported));
00601 }
00602 return new Canvas(width, height, imap[frameworkName]);
00603 }
00604
00608 public static IEnumerator<string> AvailableFrameworks {
00609 get {return imap.Keys.GetEnumerator();}
00610 }
00611
00618
00619 public static Canvas Adapter(object existingCanvas, double animationInterval) {
00620 if (existingCanvas is System.Windows.Forms.Control) {
00621 System.Windows.Forms.Control control = existingCanvas as System.Windows.Forms.Control;
00622 return new Canvas(control.Width, control.Height,
00623 new WFormsCanvasImplementation(control, (int)(animationInterval * 1000)));
00624 }
00625 else
00626 throw new ArgumentException(string.Format("Adapter does not exist for class {0}", existingCanvas.GetType().FullName));
00627 }
00628 }
00629
00630
00631 }