25#pragma warning disable 649
28 private Gtk.DrawingArea view;
29#pragma warning restore 649
30 private AForge.Color selectColor;
31 private Builder _builder;
38 Builder builder =
new Builder(
new FileStream(System.IO.Path.GetDirectoryName(Environment.ProcessPath) +
"/" +
"Glade/Tools.glade", FileMode.Open));
39 return new Tools(builder, builder.GetObject(
"tools").Handle);
42 protected Tools(Builder builder, IntPtr handle) : base(handle)
44 selectColor = AForge.Color.FromArgb(0, 0, 150, 255);
45 AForge.Color white = AForge.Color.FromArgb(255, 255, 255, 255);
47 builder.Autoconnect(
this);
50 ColorS col =
new ColorS(ushort.MaxValue);
52 currentTool =
GetTool(Tool.Type.move.ToString());
53 floodFiller =
new QueueLinearFloodFiller(floodFiller);
55 App.ApplyStyles(
this);
57 public static bool applyToStack =
false;
58 public static ColorTool colorTool;
59 public static bool rEnabled =
true;
60 public static bool gEnabled =
true;
61 public static bool bEnabled =
true;
62 public static ColorS drawColor =
new ColorS(ushort.MaxValue, ushort.MaxValue, ushort.MaxValue);
63 public static ColorS eraseColor =
new ColorS(0, 0, 0);
64 public static ColorS tolerance =
new ColorS(0, 0, 0);
66 private static int width = 5;
67 private static ROI roi;
68 public static ROI selectedROI
72 List<ROI> rs =
new List<ROI>();
73 foreach (var item
in GetSelectableRois(ImageView.SelectedImage))
74 if (item !=
null && item.Selected)
85 public static Rectangle selectionRectangle;
86 public static Dictionary<string, Tool> tools =
new Dictionary<string, Tool>();
87 private static AbstractFloodFiller floodFiller =
null;
88 static List<ROI> rois =
new List<ROI>();
90 public static List<ROI> selectedROIs
95 for (
int i = 0; i < Images.images.Count; i++)
97 foreach (var item
in GetSelectableRois(Images.images[i]))
98 if (item !=
null && item.Selected)
106 rois.AddRange(value);
109 public static bool HitTestMask(ROI ann, PointD worldPoint)
111 if (ann ==
null || ann.type != ROI.Type.Mask || ann.roiMask ==
null)
118 if (worldPoint.X < x || worldPoint.Y < y ||
119 worldPoint.X > x + w || worldPoint.Y > y + h)
122 if (ann.roiMask.PhysicalSizeX == 0 || ann.roiMask.PhysicalSizeY == 0)
125 int mx = (int)Math.Floor((worldPoint.X - x) / ann.roiMask.PhysicalSizeX);
126 int my = (int)Math.Floor((worldPoint.Y - y) / ann.roiMask.PhysicalSizeY);
127 if (mx < 0 || my < 0 || mx >= ann.roiMask.Width || my >= ann.roiMask.Height)
130 return ann.roiMask.IsSelected(mx, my);
134 if (ImageView.SelectedImage.isPyramidal)
135 roi.serie = App.viewer.Level;
137 roi.serie = ImageView.SelectedImage.series;
138 roi.coord = App.viewer.GetCoordinate();
139 ImageView.SelectedImage.Annotations.Add(roi);
145 this.ButtonPressEvent += Tools_ButtonPressEvent;
147 view.Drawn += View_Drawn;
148 view.DeleteEvent += View_DeleteEvent;
150 private void View_DeleteEvent(
object o, DeleteEventArgs args)
156 static int gridW = 2;
157 static int gridH = 11;
159 static List<Rectangle> rects =
new List<Rectangle>();
164 private void View_Drawn(
object o, DrawnArgs e)
170 foreach (Tool item
in tools.Values)
172 if (item.image !=
null)
175 Pixbuf pf = item.image.ScaleSimple(w, h, InterpType.Bilinear);
178 e.Cr.SetSourceRGB(item.color.Rf, item.color.Gf, item.color.Bf);
179 e.Cr.Rectangle(x * w, y * h, w, h);
182 Gdk.CairoHelper.SetSourcePixbuf(e.Cr, pf, w * x, h * y);
189 e.Cr.SetSourceRGB(item.color.Rf, item.color.Gf, item.color.Bf);
190 e.Cr.Rectangle(x * w, y * h, w, h);
212 private void Tools_ButtonPressEvent(
object o, ButtonPressEventArgs args)
214 Gdk.Rectangle r =
new Gdk.Rectangle((
int)args.Event.X, (
int)args.Event.Y, 1, 1);
216 foreach (Tool t
in tools.Values)
218 if (t.bounds.IntersectsWith(
new AForge.PointD(args.Event.X, args.Event.Y)))
231 if (tool.type == Tool.Type.color1)
233 if (tool.type == Tool.Type.color2)
235 if (tool.type == Tool.Type.switchColors)
239 public static bool colorOne =
true;
243 private void Color1Click()
245 if (App.color !=
null)
246 if (App.color.Visible)
249 App.color = ColorTool.Create(
false);
253 private void Color2Click()
255 if (App.color !=
null)
256 if (App.color.Visible)
259 App.color = ColorTool.Create(
true);
263 private void SwitchClick()
265 ColorS s = DrawColor;
266 DrawColor = EraseColor;
269 private void UpdateView()
271 App.viewer.UpdateView();
275 private void SyncPointsFromBoundingBox(ROI roi)
277 if (roi ==
null)
return;
278 RectangleD r = roi.BoundingBox;
280 double left = Math.Min(r.X, r.X + r.W);
281 double top = Math.Min(r.Y, r.Y + r.H);
282 double right = Math.Max(r.X, r.X + r.W);
283 double bottom = Math.Max(r.Y, r.Y + r.H);
286 roi.BoundingBox =
new RectangleD(left, top, right - left, bottom - top);
289 if (roi.Points ==
null) roi.Points =
new List<PointD>();
291 if (roi.Points.Count < 4)
294 roi.Points.Add(
new PointD(left, top));
295 roi.Points.Add(
new PointD(right, top));
296 roi.Points.Add(
new PointD(right, bottom));
297 roi.Points.Add(
new PointD(left, bottom));
302 roi.UpdatePoint(
new PointD(left, top), 0);
303 roi.UpdatePoint(
new PointD(right, top), 1);
304 roi.UpdatePoint(
new PointD(right, bottom), 2);
305 roi.UpdatePoint(
new PointD(left, bottom), 3);
310 private void SyncBoundingBoxFromPoints(ROI roi)
312 if (roi ==
null || roi.Points ==
null || roi.Points.Count == 0)
return;
314 double minx =
double.MaxValue, miny =
double.MaxValue;
315 double maxx =
double.MinValue, maxy =
double.MinValue;
317 foreach (var p
in roi.Points)
319 if (p.X < minx) minx = p.X;
320 if (p.Y < miny) miny = p.Y;
321 if (p.X > maxx) maxx = p.X;
322 if (p.Y > maxy) maxy = p.Y;
325 roi.BoundingBox =
new RectangleD(minx, miny, maxx - minx, maxy - miny);
332 public void ToolDown(PointD e, ButtonPressEventArgs buts)
334 if (App.viewer ==
null || currentTool ==
null || ImageView.SelectedImage ==
null)
337 Plugins.MouseDown(ImageView.SelectedImage, e, buts);
338 Scripting.UpdateState(Scripting.State.GetDown(e, buts.Event.Button));
340 switch (currentTool.type)
343 ToolDown_Pan(e, buts);
345 case Tool.Type.magic:
346 ToolDown_Magic(e, buts);
348 case Tool.Type.point:
349 ToolDown_Point(e, buts);
352 ToolDown_Move(e, buts);
355 ToolDown_Line(e, buts);
358 ToolDown_Rectangle(e, buts);
360 case Tool.Type.ellipse:
361 ToolDown_Ellipse(e, buts);
363 case Tool.Type.polygon:
364 ToolDown_Polygon(e, buts);
367 ToolDown_Text(e, buts);
369 case Tool.Type.delete:
370 ToolDown_Delete(e, buts);
372 case Tool.Type.freeform:
373 ToolDown_Freeform(e, buts);
380 public void ToolUp(PointD e, ButtonReleaseEventArgs buts)
382 if (App.viewer ==
null || currentTool ==
null || ImageView.SelectedImage ==
null)
385 Plugins.MouseUp(ImageView.SelectedImage, e, buts);
386 Scripting.UpdateState(Scripting.State.GetUp(e, buts.Event.Button));
388 switch (currentTool.type)
394 ToolUp_Move(e, buts);
396 case Tool.Type.magic:
397 ToolUp_Magic(e, buts);
400 ToolUp_Line(e, buts);
403 ToolUp_Rectangle(e, buts);
405 case Tool.Type.ellipse:
406 ToolUp_Ellipse(e, buts);
408 case Tool.Type.polygon:
409 ToolUp_Polygon(e, buts);
414 case Tool.Type.delete:
417 case Tool.Type.freeform:
418 ToolUp_Freeform(e, buts);
420 case Tool.Type.bucket:
421 ToolUp_Bucket(e, buts);
423 case Tool.Type.dropper:
424 ToolUp_Dropper(e, buts);
431 public void ToolMove(PointD e, MotionNotifyEventArgs buts)
433 if (App.viewer ==
null)
436 Plugins.MouseMove(ImageView.SelectedImage, e, buts);
439 if (buts.Event.State.HasFlag(ModifierType.Button1Mask))
440 Scripting.UpdateState(Scripting.State.GetMove(e, 1));
441 else if (buts.Event.State.HasFlag(ModifierType.Button2Mask))
442 Scripting.UpdateState(Scripting.State.GetMove(e, 2));
443 else if (buts.Event.State.HasFlag(ModifierType.Button3Mask))
444 Scripting.UpdateState(Scripting.State.GetMove(e, 3));
445 else if (buts.Event.State.HasFlag(ModifierType.Button4Mask))
446 Scripting.UpdateState(Scripting.State.GetMove(e, 4));
447 else if (buts.Event.State.HasFlag(ModifierType.Button5Mask))
448 Scripting.UpdateState(Scripting.State.GetMove(e, 5));
450 Scripting.UpdateState(Scripting.State.GetMove(e, 0));
452 switch (currentTool.type)
455 ToolMove_Pan(e, buts);
458 ToolMove_Move(e, buts);
460 case Tool.Type.magic:
461 ToolMove_Magic(e, buts);
464 ToolMove_Line(e, buts);
467 ToolMove_Rectangle(e, buts);
469 case Tool.Type.ellipse:
470 ToolMove_Ellipse(e, buts);
472 case Tool.Type.polygon:
473 ToolMove_Polygon(e, buts);
478 case Tool.Type.freeform:
479 ToolMove_Freeform(e, buts);
481 case Tool.Type.brush:
482 ToolMove_Brush(e, buts);
484 case Tool.Type.eraser:
485 ToolMove_Eraser(e, buts);
495 public void ToolDown_Move(PointD e, ButtonPressEventArgs buts)
497 if (currentTool.type != Tool.Type.move || buts.Event.Button != 1)
500 App.viewer.MouseDown = e;
501 bool ctrl = buts.Event.State.HasFlag(ModifierType.ControlMask);
502 var selectableRois = GetSelectableRois(ImageView.SelectedImage).ToList();
503 var selectedRois = selectableRois.Where(r => r !=
null && r.Selected).Reverse().ToList();
504 var unselectedRois = selectableRois.Where(r => r !=
null && !r.Selected).Reverse().ToList();
509 foreach (var ann
in selectedRois)
511 if (ann.type == ROI.Type.Mask)
514 var boxes = ann.GetSelectBoxes(ROI.selectBoxSize * ImageView.SelectedImage.PhysicalSizeX);
515 if (boxes ==
null)
continue;
517 for (
int i = 0; i < boxes.Length; i++)
519 if (boxes[i].Contains(ann.coord, e))
524 if (ann.selectedPoints.Contains(i)) ann.selectedPoints.Remove(i);
525 else ann.selectedPoints.Add(i);
529 ann.selectedPoints.Clear();
530 ann.selectedPoints.Add(i);
539 foreach (var ann
in selectedRois)
544 if (ann.type == ROI.Type.Mask)
546 if (HitTestMask(ann, e))
555 if (ann.BoundingBox.Contains(ann.coord, e))
564 foreach (var ann
in selectedRois.Concat(unselectedRois))
569 bool hit = ann.type == ROI.Type.Mask ? HitTestMask(ann, e) : ann.BoundingBox.Contains(ann.coord, e);
574 foreach (var other
in selectableRois)
578 other.Selected =
false;
579 other.selectedPoints?.Clear();
585 ann.selectedPoints?.Clear();
595 foreach (var ann
in selectableRois)
599 ann.Selected =
false;
600 ann.selectedPoints?.Clear();
606 Tools.GetTool(Tools.Tool.Type.move).Rectangle =
new RectangleD(e.X, e.Y, 0, 0);
610 private static IEnumerable<ROI> GetSelectableRois(BioImage image)
615 if (image.Annotations !=
null)
617 foreach (var roi
in image.Annotations)
622 if (App.viewer ==
null)
625 foreach (var roi
in App.viewer.GetZarrLabelOverlaysForImage(image))
629 public void ToolMove_Move(PointD e, MotionNotifyEventArgs buts)
631 if (currentTool.type != Tool.Type.move ||
632 !buts.Event.State.HasFlag(ModifierType.Button1Mask))
636 if (selectedROI !=
null && selectedROI.Selected && selectedROI.selectedPoints !=
null)
638 PointD delta =
new PointD(e.X - App.viewer.MouseDown.X, e.Y - App.viewer.MouseDown.Y);
641 if (selectedROI.type == ROI.Type.Rectangle || selectedROI.type == ROI.Type.Ellipse)
644 if (selectedROI.selectedPoints.Count == 1)
646 int pointIndex = selectedROI.selectedPoints[0];
647 RectangleD rect = selectedROI.BoundingBox;
650 double left = rect.X;
652 double right = rect.X + rect.W;
653 double bottom = rect.Y + rect.H;
678 RectangleD newRect =
new RectangleD(
679 Math.Min(left, right),
680 Math.Min(top, bottom),
681 Math.Abs(right - left),
682 Math.Abs(bottom - top)
685 selectedROI.BoundingBox = newRect;
688 SyncPointsFromBoundingBox(selectedROI);
689 selectedROI.UpdateBoundingBox();
691 App.viewer.MouseDown =
new PointD(e.X, e.Y);
692 App.viewer.UpdateView();
698 for (
int i = 0; i < selectedROI.Points.Count; i++)
700 selectedROI.UpdatePoint(
701 new PointD(selectedROI.Points[i].X + delta.X,
702 selectedROI.Points[i].Y + delta.Y), i);
706 SyncBoundingBoxFromPoints(selectedROI);
707 selectedROI.UpdateBoundingBox();
709 App.viewer.MouseDown =
new PointD(e.X, e.Y);
710 App.viewer.UpdateView();
717 if (selectedROI.selectedPoints.Count == 0)
720 for (
int i = 0; i < selectedROI.Points.Count; i++)
722 selectedROI.UpdatePoint(
723 new PointD(selectedROI.Points[i].X + delta.X,
724 selectedROI.Points[i].Y + delta.Y), i);
730 foreach (
int pointIndex
in selectedROI.selectedPoints.ToArray())
732 if (pointIndex >= 0 && pointIndex < selectedROI.Points.Count)
734 PointD pt = selectedROI.Points[pointIndex];
735 selectedROI.UpdatePoint(
736 new PointD(pt.X + delta.X, pt.Y + delta.Y), pointIndex);
742 SyncBoundingBoxFromPoints(selectedROI);
743 selectedROI.UpdateBoundingBox();
745 App.viewer.MouseDown =
new PointD(e.X, e.Y);
746 App.viewer.UpdateView();
751 else if (selectedROI !=
null && selectedROI.Selected)
753 PointD delta =
new PointD(e.X - App.viewer.MouseDown.X, e.Y - App.viewer.MouseDown.Y);
755 for (
int i = 0; i < selectedROI.Points.Count; i++)
757 selectedROI.UpdatePoint(
758 new PointD(selectedROI.Points[i].X + delta.X,
759 selectedROI.Points[i].Y + delta.Y), i);
762 SyncBoundingBoxFromPoints(selectedROI);
763 selectedROI.UpdateBoundingBox();
765 App.viewer.MouseDown =
new PointD(e.X, e.Y);
766 App.viewer.UpdateView();
772 PointD d =
new PointD(e.X - App.viewer.MouseDown.X, e.Y - App.viewer.MouseDown.Y);
773 RectangleD selRect =
new RectangleD(
774 Math.Min(App.viewer.MouseDown.X, e.X),
775 Math.Min(App.viewer.MouseDown.Y, e.Y),
776 Math.Abs(d.X), Math.Abs(d.Y));
778 Tools.GetTool(Tools.Tool.Type.move).Rectangle = selRect;
780 bool controlHeld = buts.Event.State.HasFlag(ModifierType.ControlMask);
783 foreach (ROI an
in ImageView.SelectedImage.Annotations)
785 if (an ==
null)
continue;
787 RectangleD selBound = an.GetSelectBound(
788 ROI.selectBoxSize * ImageView.SelectedImage.PhysicalSizeX,
789 ROI.selectBoxSize * ImageView.SelectedImage.PhysicalSizeY);
791 if (selBound.IntersectsWith(selRect))
796 an.selectedPoints?.Clear();
799 RectangleD[] sels = an.GetSelectBoxes(
800 ROI.selectBoxSize * ImageView.SelectedImage.PhysicalSizeX);
803 for (
int i = 0; i < sels.Length; i++)
805 if (sels[i].IntersectsWith(selRect))
807 if (an.selectedPoints ==
null) an.selectedPoints =
new List<int>();
808 if (!an.selectedPoints.Contains(i))
809 an.selectedPoints.Add(i);
814 else if (!controlHeld)
817 an.selectedPoints?.Clear();
821 if (selectedROI !=
null)
822 selectedROI.UpdateBoundingBox();
824 App.viewer.UpdateView();
827 public void ToolUp_Move(PointD e, ButtonReleaseEventArgs buts)
829 if (currentTool.type != Tool.Type.move || buts.Event.Button != 1)
833 Tools.GetTool(Tools.Tool.Type.move).Rectangle =
new RectangleD(0, 0, 0, 0);
834 selectedROI?.UpdateBoundingBox();
835 App.viewer.UpdateView();
842 public void ToolDown_Point(PointD e, ButtonPressEventArgs buts)
844 if (currentTool.type != Tool.Type.point || buts.Event.Button != 1)
848 an.type = ROI.Type.Point;
850 an.Points.Add(
new PointD(e.X, e.Y));
851 an.coord = App.viewer.GetCoordinate();
852 an.serie = ImageView.SelectedImage.isPyramidal ? App.viewer.Level : ImageView.SelectedImage.series;
862 public void ToolDown_Line(PointD e, ButtonPressEventArgs buts)
864 if (currentTool.type != Tool.Type.line || buts.Event.Button != 1)
867 selectedROI =
new ROI();
868 selectedROI.type = ROI.Type.Line;
869 selectedROI.AddPoint(
new PointD(e.X, e.Y));
870 selectedROI.AddPoint(
new PointD(e.X + ImageView.SelectedImage.PhysicalSizeX * ROI.selectBoxSize,
871 e.Y + ImageView.SelectedImage.PhysicalSizeX * ROI.selectBoxSize));
872 selectedROI.coord = App.viewer.GetCoordinate();
873 selectedROI.serie = ImageView.SelectedImage.isPyramidal ? App.viewer.Level : ImageView.SelectedImage.series;
877 public void ToolMove_Line(PointD e, MotionNotifyEventArgs buts)
879 if (currentTool.type != Tool.Type.line ||
880 !buts.Event.State.HasFlag(ModifierType.Button1Mask))
883 if (selectedROI !=
null)
885 if (selectedROI.selectedPoints.Count == 1)
887 selectedROI.UpdatePoint(
new PointD(e.X, e.Y), selectedROI.selectedPoints[0]);
888 selectedROI.UpdateBoundingBox();
893 selectedROI.UpdatePoint(
new PointD(e.X, e.Y), 1);
894 selectedROI.UpdateBoundingBox();
900 public void ToolUp_Line(PointD e, ButtonReleaseEventArgs buts)
902 if (currentTool.type != Tool.Type.line || buts.Event.Button != 1)
905 if (selectedROI !=
null && selectedROI.GetPointCount() >= 2)
907 selectedROI.UpdatePoint(
new PointD(e.X, e.Y), 1);
908 selectedROI.UpdateBoundingBox();
914 public void ToolDown_Rectangle(PointD e, ButtonPressEventArgs buts)
916 if (currentTool.type != Tool.Type.rect || buts.Event.Button != 1)
919 selectedROI =
new ROI();
920 selectedROI.type = ROI.Type.Rectangle;
921 selectedROI.BoundingBox =
new RectangleD(e.X, e.Y, 0, 0);
922 selectedROI.coord = App.viewer.GetCoordinate();
923 selectedROI.serie = ImageView.SelectedImage.isPyramidal ? App.viewer.Level : ImageView.SelectedImage.series;
927 public void ToolMove_Rectangle(PointD e, MotionNotifyEventArgs buts)
929 if (currentTool.type != Tool.Type.rect ||
930 !buts.Event.State.HasFlag(ModifierType.Button1Mask))
933 if (selectedROI !=
null && selectedROI.type == ROI.Type.Rectangle)
935 selectedROI.BoundingBox = RectangleDExtensions.FromCorners(
936 new PointD(selectedROI.BoundingBox.X, selectedROI.BoundingBox.Y),
937 new PointD(e.X, e.Y));
942 public void ToolUp_Rectangle(PointD e, ButtonReleaseEventArgs buts)
944 if (currentTool.type != Tool.Type.rect || buts.Event.Button != 1)
947 if (selectedROI !=
null && selectedROI.type == ROI.Type.Rectangle)
950 selectedROI.BoundingBox = RectangleDExtensions.FromCorners(
951 new PointD(selectedROI.BoundingBox.X, selectedROI.BoundingBox.Y),
952 new PointD(e.X, e.Y));
955 selectedROI.Validate();
964 public void ToolDown_Ellipse(PointD e, ButtonPressEventArgs buts)
966 if (currentTool.type != Tool.Type.ellipse || buts.Event.Button != 1)
969 selectedROI =
new ROI();
970 selectedROI.type = ROI.Type.Ellipse;
971 selectedROI.BoundingBox =
new RectangleD(e.X, e.Y, 0, 0);
972 selectedROI.coord = App.viewer.GetCoordinate();
973 selectedROI.serie = ImageView.SelectedImage.isPyramidal ? App.viewer.Level : ImageView.SelectedImage.series;
977 public void ToolMove_Ellipse(PointD e, MotionNotifyEventArgs buts)
979 if (currentTool.type != Tool.Type.ellipse ||
980 !buts.Event.State.HasFlag(ModifierType.Button1Mask))
983 if (selectedROI !=
null && selectedROI.type == ROI.Type.Ellipse)
985 selectedROI.BoundingBox = RectangleDExtensions.FromCorners(
986 new PointD(selectedROI.BoundingBox.X, selectedROI.BoundingBox.Y),
987 new PointD(e.X, e.Y));
992 public void ToolUp_Ellipse(PointD e, ButtonReleaseEventArgs buts)
994 if (currentTool.type != Tool.Type.ellipse || buts.Event.Button != 1)
997 if (selectedROI !=
null && selectedROI.type == ROI.Type.Ellipse)
1000 selectedROI.BoundingBox = RectangleDExtensions.FromCorners(
1001 new PointD(selectedROI.BoundingBox.X, selectedROI.BoundingBox.Y),
1002 new PointD(e.X, e.Y));
1005 selectedROI.Validate();
1014 public void ToolDown_Polygon(PointD e, ButtonPressEventArgs buts)
1016 if (currentTool.type != Tool.Type.polygon || buts.Event.Button != 1)
1019 if (selectedROI ==
null || selectedROI.type != ROI.Type.Polygon)
1022 selectedROI =
new ROI();
1023 selectedROI.type = ROI.Type.Polygon;
1024 selectedROI.closed =
false;
1025 selectedROI.AddPoint(
new PointD(e.X, e.Y));
1026 selectedROI.coord = App.viewer.GetCoordinate();
1027 selectedROI.serie = ImageView.SelectedImage.isPyramidal ? App.viewer.Level : ImageView.SelectedImage.series;
1028 AddROI(selectedROI);
1031 else if (!selectedROI.closed)
1034 if (selectedROI.Points.Count >= 3)
1036 PointD firstPoint = selectedROI.Points[0];
1037 double distance = Math.Sqrt(Math.Pow(e.X - firstPoint.X, 2) + Math.Pow(e.Y - firstPoint.Y, 2));
1038 double threshold = 10.0;
1040 if (distance < threshold)
1043 selectedROI.closed =
true;
1044 selectedROI.Selected =
false;
1045 selectedROI.UpdateBoundingBox();
1046 selectedROI.Validate();
1056 selectedROI.AddPoint(
new PointD(e.X, e.Y));
1057 selectedROI.UpdateBoundingBox();
1061 private void ToolMove_Polygon(PointD e, MotionNotifyEventArgs buts)
1063 if (currentTool.type != Tool.Type.polygon)
1067 if (selectedROI !=
null && selectedROI.type == ROI.Type.Polygon && !selectedROI.closed)
1074 private void ToolUp_Polygon(PointD e, ButtonReleaseEventArgs buts)
1083 public void ToolDown_Freeform(PointD e, ButtonPressEventArgs buts)
1085 if (currentTool.type != Tool.Type.freeform || buts.Event.Button != 1)
1088 if (selectedROI ==
null || selectedROI.type != ROI.Type.Polygon)
1091 selectedROI =
new ROI();
1092 selectedROI.type = ROI.Type.Polygon;
1093 selectedROI.closed =
false;
1094 selectedROI.AddPoint(
new PointD(e.X, e.Y));
1095 selectedROI.coord = App.viewer.GetCoordinate();
1096 selectedROI.serie = ImageView.SelectedImage.isPyramidal ? App.viewer.Level : ImageView.SelectedImage.series;
1097 AddROI(selectedROI);
1100 else if (!selectedROI.closed)
1103 if (selectedROI.Points.Count >= 3)
1105 PointD firstPoint = selectedROI.Points[0];
1106 double distance = Math.Sqrt(Math.Pow(e.X - firstPoint.X, 2) + Math.Pow(e.Y - firstPoint.Y, 2));
1107 double threshold = 10.0;
1109 if (distance < threshold)
1112 selectedROI.closed =
true;
1113 selectedROI.Selected =
false;
1114 selectedROI.UpdateBoundingBox();
1115 selectedROI.Validate();
1125 selectedROI.AddPoint(
new PointD(e.X, e.Y));
1126 selectedROI.UpdateBoundingBox();
1130 public void ToolMove_Freeform(PointD e, MotionNotifyEventArgs buts)
1132 if (selectedROI !=
null)
1133 if (currentTool.type == Tool.Type.freeform && buts.Event.State.HasFlag(ModifierType.Button1Mask))
1135 selectedROI.AddPoint(
new PointD(e.X, e.Y));
1136 selectedROI.coord = App.viewer.GetCoordinate();
1137 selectedROI.serie = ImageView.SelectedImage.isPyramidal ? App.viewer.Level : ImageView.SelectedImage.series;
1141 public void ToolUp_Freeform(PointD e, ButtonReleaseEventArgs buts)
1143 if (currentTool.type != Tool.Type.freeform || buts.Event.Button != 1)
1146 if (selectedROI !=
null)
1148 selectedROI.UpdateBoundingBox();
1157 public void ToolDown_Text(PointD e, ButtonPressEventArgs buts)
1159 if (currentTool.type != Tool.Type.text || buts.Event.Button != 1)
1162 selectedROI =
new ROI();
1163 selectedROI.type = ROI.Type.Label;
1164 selectedROI.AddPoint(
new PointD(e.X, e.Y));
1165 selectedROI.coord = App.viewer.GetCoordinate();
1166 selectedROI.serie = ImageView.SelectedImage.isPyramidal ? App.viewer.Level : ImageView.SelectedImage.series;
1173 AddROI(selectedROI);
1174 selectedROI.UpdateBoundingBox();
1181 public void ToolDown_Delete(PointD e, ButtonPressEventArgs buts)
1183 if (currentTool.type != Tool.Type.delete || buts.Event.Button != 1)
1186 for (
int i = ImageView.SelectedImage.Annotations.Count - 1; i >= 0; i--)
1188 ROI an = ImageView.SelectedImage.Annotations[i];
1189 if (an ==
null)
continue;
1191 if (an.BoundingBox.IntersectsWith(
new RectangleD(e.X, e.Y, 1, 1)))
1193 if (an.selectedPoints ==
null || an.selectedPoints.Count == 0)
1196 ImageView.SelectedImage.Annotations.RemoveAt(i);
1199 else if (an.selectedPoints.Count == 1 &&
1200 !(an.type == ROI.Type.Polygon ||
1201 an.type == ROI.Type.Polyline ||
1202 an.type == ROI.Type.Freeform))
1205 ImageView.SelectedImage.Annotations.RemoveAt(i);
1211 if (an.type == ROI.Type.Polygon ||
1212 an.type == ROI.Type.Polyline ||
1213 an.type == ROI.Type.Freeform)
1216 an.RemovePoints(an.selectedPoints.ToArray());
1217 an.UpdateBoundingBox();
1229 public void ToolDown_Brush(PointD e, ButtonPressEventArgs buts)
1231 if (currentTool.type != Tool.Type.brush || buts.Event.Button != 1)
1234 PointD ip = ImageView.SelectedImage.ToImageSpace(e);
1235 if (ImageView.SelectedImage.isPyramidal)
1236 ip =
new PointD(e.X, e.Y);
1238 Bio.Graphics.Graphics g =
Bio.Graphics.Graphics.FromImage(ImageView.SelectedBuffer);
1239 g.pen =
new Bio.Graphics.Pen(DrawColor, (
int)StrokeWidth, ImageView.SelectedImage.bitsPerPixel);
1240 g.FillEllipse(
new Rectangle((
int)ip.X, (
int)ip.Y, (
int)StrokeWidth, (
int)StrokeWidth), g.pen.color);
1242 App.viewer.UpdateImage();
1243 App.viewer.UpdateView();
1246 public void ToolMove_Brush(PointD e, MotionNotifyEventArgs buts)
1248 if (currentTool.type != Tool.Type.brush ||
1249 !buts.Event.State.HasFlag(ModifierType.Button1Mask))
1252 PointD ip = ImageView.SelectedImage.ToImageSpace(e);
1253 if (ImageView.SelectedImage.isPyramidal)
1254 ip =
new PointD(e.X, e.Y);
1256 Bio.Graphics.Graphics g =
Bio.Graphics.Graphics.FromImage(ImageView.SelectedBuffer);
1257 g.pen =
new Bio.Graphics.Pen(DrawColor, (
int)StrokeWidth, ImageView.SelectedImage.bitsPerPixel);
1258 g.FillEllipse(
new Rectangle((
int)ip.X, (
int)ip.Y, (
int)StrokeWidth, (
int)StrokeWidth), g.pen.color);
1260 App.viewer.UpdateImages();
1261 App.viewer.UpdateView();
1267 public void ToolDown_Eraser(PointD e, ButtonPressEventArgs buts)
1269 if (currentTool.type != Tool.Type.eraser || buts.Event.Button != 1)
1272 PointD ip = ImageView.SelectedImage.ToImageSpace(e);
1273 if (ImageView.SelectedImage.isPyramidal)
1274 ip =
new PointD(e.X, e.Y);
1276 Bio.Graphics.Graphics g =
Bio.Graphics.Graphics.FromImage(ImageView.SelectedBuffer);
1277 Bio.Graphics.Pen pen =
new Bio.Graphics.Pen(EraseColor, (
int)StrokeWidth, ImageView.SelectedBuffer.BitsPerPixel);
1278 g.FillEllipse(
new Rectangle((
int)ip.X, (
int)ip.Y, (
int)StrokeWidth, (
int)StrokeWidth), pen.color);
1280 App.viewer.UpdateImages();
1281 App.viewer.UpdateView();
1284 public void ToolMove_Eraser(PointD e, MotionNotifyEventArgs buts)
1286 if (currentTool.type != Tool.Type.eraser ||
1287 !buts.Event.State.HasFlag(ModifierType.Button1Mask))
1290 PointD ip = ImageView.SelectedImage.ToImageSpace(e);
1291 if (ImageView.SelectedImage.isPyramidal)
1292 ip =
new PointD(e.X, e.Y);
1294 Bio.Graphics.Graphics g =
Bio.Graphics.Graphics.FromImage(ImageView.SelectedBuffer);
1295 Bio.Graphics.Pen pen =
new Bio.Graphics.Pen(EraseColor, (
int)StrokeWidth, ImageView.SelectedBuffer.BitsPerPixel);
1296 g.FillEllipse(
new Rectangle((
int)ip.X, (
int)ip.Y, (
int)StrokeWidth, (
int)StrokeWidth), pen.color);
1298 App.viewer.UpdateImages();
1299 App.viewer.UpdateView();
1305 public void ToolUp_Bucket(PointD e, ButtonReleaseEventArgs buts)
1307 if (currentTool.type != Tool.Type.bucket || buts.Event.Button != 1)
1310 PointD ip = ImageView.SelectedImage.ToImageSpace(e);
1312 if (ip.X >= ImageView.SelectedImage.SizeX || ip.Y >= ImageView.SelectedImage.SizeY)
1315 floodFiller.FillColor = DrawColor;
1316 floodFiller.Tolerance = tolerance;
1317 floodFiller.Bitmap = ImageView.SelectedBuffer;
1318 floodFiller.FloodFill(
new AForge.Point((
int)ip.X, (
int)ip.Y));
1320 App.viewer.UpdateImages();
1321 App.viewer.UpdateView();
1327 public void ToolUp_Dropper(PointD e, ButtonReleaseEventArgs buts)
1329 if (currentTool.type != Tool.Type.dropper || buts.Event.Button != 1)
1332 PointD ip = ImageView.SelectedImage.ToImageSpace(e);
1334 if (ip.X >= 0 && ip.Y >= 0 && ip.X < ImageView.SelectedImage.SizeX && ip.Y < ImageView.SelectedImage.SizeY)
1336 DrawColor = ImageView.SelectedBuffer.GetPixel((
int)ip.X, (
int)ip.Y);
1337 App.viewer.UpdateView();
1347 private bool isPanning;
1348 private double panStartX;
1349 private double panStartY;
1351 private PointD initialPanOrigin;
1352 private double initialPanScale;
1355 private PointD panVelocity;
1356 private DateTime lastMoveTime;
1359 private const double PanFriction = 50.0;
1360 private const double VelocityEpsilon = 0.0001;
1363 private double dpiScale = 1.0;
1366 private bool kineticActive =
true;
1369 public static double PanSpeed = 0.75;
1373 public void ToolDown_Pan(PointD e, ButtonPressEventArgs buts)
1375 if (buts.Event.Button != 1 && buts.Event.Button != 2)
1378 currentTool =
GetTool(Tool.Type.pan);
1379 panStartX = buts.Event.X;
1380 panStartY = buts.Event.Y;
1381 initialPanScale = ImageView.SelectedImage.isPyramidal
1382 ? App.viewer.Resolution
1383 : App.viewer.Scale.Width;
1385 if (ImageView.SelectedImage.isPyramidal)
1387 initialPanOrigin =
new PointD(
1388 App.viewer.PyramidalOrigin.X,
1389 App.viewer.PyramidalOrigin.Y);
1393 initialPanOrigin =
new PointD(
1394 App.viewer.Origin.X,
1395 App.viewer.Origin.Y);
1402 public void ToolMove_Pan(PointD e, MotionNotifyEventArgs buts)
1404 if (currentTool.type != Tool.Type.pan)
1407 if (!buts.Event.State.HasFlag(ModifierType.Button1Mask) &&
1408 !buts.Event.State.HasFlag(ModifierType.Button2Mask))
1411 double dx = buts.Event.X - panStartX;
1412 double dy = buts.Event.Y - panStartY;
1414 if (ImageView.SelectedImage.isPyramidal)
1420 double deltaX = dx * App.viewer.Resolution * PanSpeed;
1421 double deltaY = dy * App.viewer.Resolution * PanSpeed;
1422 App.viewer.PyramidalOrigin =
new PointD(
1423 initialPanOrigin.X - deltaX,
1424 initialPanOrigin.Y - deltaY);
1430 double scale = Math.Max(initialPanScale,
double.Epsilon);
1431 double deltaX = dx / (App.viewer.PxWmicron * scale);
1432 double deltaY = dy / (App.viewer.PxHmicron * scale);
1433 App.viewer.Origin =
new PointD(
1434 initialPanOrigin.X + deltaX,
1435 initialPanOrigin.Y + deltaY);
1443 public void ToolUp_Pan(PointD e, ButtonReleaseEventArgs buts)
1445 if (currentTool.type == Tool.Type.pan &&
1446 (buts.Event.Button == 1 || buts.Event.Button == 2))
1448 currentTool =
GetTool(Tool.Type.move);
1451 private void StartKineticLoop()
1453 DateTime last = DateTime.UtcNow;
1455 GLib.Timeout.Add(16, () =>
1460 DateTime now = DateTime.UtcNow;
1461 double dt = (now - last).TotalSeconds;
1464 double speed = Math.Sqrt(
1465 panVelocity.X * panVelocity.X +
1466 panVelocity.Y * panVelocity.Y);
1468 if (speed < VelocityEpsilon)
1470 kineticActive =
false;
1475 double decel = PanFriction * dt;
1476 double scale = Math.Max(0, (speed - decel) / speed);
1478 panVelocity =
new PointD(
1479 panVelocity.X * scale,
1480 panVelocity.Y * scale);
1482 PointD origin = GetOrigin();
1483 origin =
new PointD(
1484 origin.X + panVelocity.X * dt,
1485 origin.Y + panVelocity.Y * dt);
1487 origin = ClampOrigin(origin);
1493 private PointD ClampOrigin(PointD origin)
1495 var img = ImageView.SelectedImage;
1496 var res0 = img.Resolutions[0];
1498 double viewW = App.viewer.ImageViewWidth / App.viewer.Resolution;
1499 double viewH = App.viewer.ImageViewHeight / App.viewer.Resolution;
1505 double minX = -(viewW);
1506 double minY = -(viewH);
1507 double maxX = res0.SizeX;
1508 double maxY = res0.SizeY;
1511 Math.Clamp(origin.X, minX, maxX),
1512 Math.Clamp(origin.Y, minY, maxY));
1514 private PointD GetOrigin()
1516 return ImageView.SelectedImage.isPyramidal
1517 ? App.viewer.PyramidalOrigin
1518 : App.viewer.Origin;
1521 private void SetOrigin(PointD p)
1523 if (ImageView.SelectedImage.isPyramidal)
1524 App.viewer.PyramidalOrigin = p;
1526 App.viewer.Origin = p;
1532 public void ToolDown_Magic(PointD e, ButtonPressEventArgs buts)
1534 if (selectedROI ==
null)
1536 selectedROI =
new ROI();
1537 selectedROI.type = ROI.Type.Freeform;
1538 selectedROI.AddPoint(
new PointD(e.X, e.Y));
1539 selectedROI.coord = App.viewer.GetCoordinate();
1540 if (ImageView.SelectedImage.isPyramidal)
1541 selectedROI.serie = App.viewer.Level;
1543 selectedROI.serie = ImageView.SelectedImage.series;
1544 AddROI(selectedROI);
1548 selectedROI.AddPoint(
new PointD(e.X, e.Y));
1552 public void ToolMove_Magic(PointD e, MotionNotifyEventArgs buts)
1555 PointD d =
new PointD(e.X - App.viewer.MouseDown.X, e.Y - App.viewer.MouseDown.Y);
1556 Tools.GetTool(Tools.Tool.Type.move).Rectangle =
new RectangleD(App.viewer.MouseDown.X, App.viewer.MouseDown.Y, d.X, d.Y);
1560 public void ToolUp_Magic(PointD e, ButtonReleaseEventArgs buts)
1562 if (!buts.Event.State.HasFlag(ModifierType.Button1Mask))
1565 RectangleD rectangle =
new RectangleD(
1566 App.viewer.MouseDown.X,
1567 App.viewer.MouseDown.Y,
1568 Math.Abs(App.viewer.MouseUp.X - App.viewer.MouseDown.X),
1569 Math.Abs(App.viewer.MouseUp.Y - App.viewer.MouseDown.Y)
1573 AForge.RectangleF rectInImageSpace = ImageView.SelectedImage.ToImageSpace(rectangle);
1574 ZCT coord = App.viewer.GetCoordinate();
1578 if (ImageView.SelectedImage.Buffers[0].RGBChannelsCount > 1)
1580 bitmap = ImageView.SelectedImage.GetFiltered(
1582 new IntRange((
int)ImageView.SelectedBuffer.Stats[0].Min, (
int)ImageView.SelectedBuffer.Stats[0].Max),
1583 new IntRange((
int)ImageView.SelectedBuffer.Stats[1].Min, (
int)ImageView.SelectedBuffer.Stats[1].Max),
1584 new IntRange((
int)ImageView.SelectedBuffer.Stats[2].Min, (
int)ImageView.SelectedBuffer.Stats[2].Max)
1589 bitmap = ImageView.SelectedImage.GetFiltered(
1591 new IntRange((
int)ImageView.SelectedBuffer.Stats[0].Min, (
int)ImageView.SelectedBuffer.Stats[0].Max),
1592 new IntRange((
int)ImageView.SelectedBuffer.Stats[0].Min, (
int)ImageView.SelectedBuffer.Stats[0].Max),
1593 new IntRange((
int)ImageView.SelectedBuffer.Stats[0].Min, (
int)ImageView.SelectedBuffer.Stats[0].Max)
1598 bitmap.Crop(rectInImageSpace.ToRectangleInt());
1600 Statistics[] st = Statistics.FromBytes(bitmap.Bytes, bitmap.SizeX, bitmap.Height, bitmap.RGBChannelsCount, bitmap.BitsPerPixel, bitmap.Stride, bitmap.PixelFormat);
1601 int threshold = magicSelect.Numeric ? magicSelect.Threshold :
CalculateThreshold(magicSelect.Index, st[0]);
1602 BlobCounter blobCounter =
new BlobCounter();
1603 OtsuThreshold th =
new OtsuThreshold();
1605 th.ApplyInPlace(bitmap);
1606 blobCounter.FilterBlobs =
true;
1607 blobCounter.MinWidth = 2;
1608 blobCounter.MinHeight = 2;
1609 blobCounter.ProcessImage(bitmap);
1611 Blob[] blobs = blobCounter.GetObjectsInformation();
1612 double pixelSizeX = ImageView.SelectedImage.PhysicalSizeX;
1613 double pixelSizeY = ImageView.SelectedImage.PhysicalSizeY;
1615 foreach (Blob blob
in blobs)
1617 AForge.RectangleD blobRectangle =
new AForge.RectangleD(
1618 blob.Rectangle.X * pixelSizeX,
1619 blob.Rectangle.Y * pixelSizeY,
1620 blob.Rectangle.Width * pixelSizeX,
1621 blob.Rectangle.Height * pixelSizeY
1624 PointD location =
new PointD(rectangle.X + blobRectangle.X, rectangle.Y + blobRectangle.Y);
1626 ROI annotation = ROI.CreateRectangle(coord, location.X, location.Y, blobRectangle.W, blobRectangle.H);
1627 ImageView.SelectedImage.Annotations.Add(annotation);
1629 App.viewer.UpdateView(
true);
1635 static int gridW = 2;
1636 static int gridH = 11;
1638 public enum ToolType
1673 string s = System.IO.Path.GetDirectoryName(Environment.ProcessPath) +
"/";
1675 if (tools.Count == 0)
1677 int x = 0;
int y = 0;
1678 foreach (
Tool.Type t in (
Tool.Type[])Enum.GetValues(typeof(
Tool.Type)))
1680 if (t != Type.script)
1681 if (t != Type.color1 && t != Type.color2)
1683 Tool tool =
new Tool(t,
new ColorS(0, 0, 0));
1684 tool.bounds =
new Rectangle(x * w, y * h, w, h);
1685 tool.image =
new Pixbuf(s +
"Images/" + tool.type.ToString() +
".png");
1686 tools.Add(tool.ToString(), tool);
1690 Tool tool =
new Tool(t,
new ColorS(0, 0, 0));
1691 tool.bounds =
new Rectangle(x * w, y * h, w, h);
1692 tools.Add(tool.ToString(), tool);
1695 if (x >
Tools.gridW - 1)
1702 DrawColor =
new ColorS(ushort.MaxValue, ushort.MaxValue, ushort.MaxValue);
1704 public List<AForge.Point> Points;
1705 public ToolType toolType;
1706 public bool selected;
1707 public Pixbuf image;
1708 public Rectangle bounds;
1709 private RectangleD rect;
1710 public RectangleD Rectangle
1712 get {
return rect; }
1713 set { rect = value; }
1715 public RectangleF RectangleF
1717 get {
return new RectangleF((
float)rect.X, (
float)rect.Y, (
float)rect.W, (
float)rect.H); }
1719 public string script;
1721 public ColorS tolerance;
1722 public ColorS color;
1725 tolerance =
new ColorS(0, 0, 0);
1730 tolerance =
new ColorS(0, 0, 0);
1732 public Tool(Type t, ColorS col)
1736 tolerance =
new ColorS(0, 0, 0);
1738 public Tool(Type t, RectangleD r)
1742 tolerance =
new ColorS(0, 0, 0);
1744 public Tool(Type t,
string sc)
1748 tolerance =
new ColorS(0, 0, 0);
1750 public override string ToString()
1752 return type.ToString();
1757 public static Tool currentTool;
1759 public static ColorS DrawColor
1768 tools[Tool.Type.color1.ToString()].color = drawColor;
1772 public static ColorS EraseColor
1781 tools[Tool.Type.color2.ToString()].color = eraseColor;
1786 public static int StrokeWidth
1798 public bool IsPanning {
get;
set; }
1800 public static RectangleD selectionRect;
1801 public static TextInput ti =
null;
1811 if (tools.Count == 0)
1813 if (tools.ContainsKey(name))
1814 return (
Tool)tools[name];
1815 else return tools[
Tool.Type.move.ToString()];
1817 public static Tool
GetTool(Tool.Type t)
1819 if (tools.ContainsKey(t.ToString()))
1820 return (Tool)tools[t.ToString()];
1821 else return tools.FirstOrDefault().Value;
1830 2 => (int)(stat.Min + stat.Mean),
1831 1 => (int)stat.Median,