WMShapes.Mod 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313
  1. MODULE WMShapes; (** AUTHOR "staubesv, PH"; PURPOSE "Basic geormetric shapes as visual components"; *)
  2. (*! to do: thread safety*)
  3. IMPORT
  4. Strings, XML, WMRectangles, WMGraphics, WMGraphicUtilities, WMProperties, WMComponents, Math, KernelLog;
  5. TYPE
  6. (* generic line. can have an arrowhead on either end *)
  7. Line* = OBJECT(WMComponents.VisualComponent)
  8. VAR
  9. color- : WMProperties.ColorProperty;
  10. colorI : WMGraphics.Color;
  11. isVertical- : WMProperties.BooleanProperty;
  12. isVerticalI : BOOLEAN;
  13. start-, end-: WMProperties.PointProperty;
  14. startI, endI: WMGraphics.Point2d;
  15. arrowAtStart-, arrowAtEnd-:WMProperties.BooleanProperty;
  16. arrowAtStartI, arrowAtEndI: BOOLEAN;
  17. PROCEDURE &Init*;
  18. BEGIN
  19. Init^;
  20. SetGenerator("WMShapes.GenLine");
  21. SetNameAsString(StrLine);
  22. NEW(color, NIL, StrColor, StrLineColorDescription); properties.Add(color);
  23. color.Set(WMGraphics.Black); colorI := color.Get();
  24. NEW(isVertical, NIL, StrIsVertical, StrIsVerticalDescription); properties.Add(isVertical);
  25. isVertical.Set(FALSE); isVerticalI := isVertical.Get();
  26. NEW(start, NIL, StrStart, StrStartDescription); properties.Add(start);
  27. start.SetCoordinate(0,0); startI := start.Get();
  28. NEW(end, NIL, StrEnd, StrEndDescription); properties.Add(end);
  29. end.SetCoordinate(100,100); endI := end.Get();
  30. NEW(arrowAtStart, NIL, StrArrowStart, StrArrowStartDescription); properties.Add(arrowAtStart);
  31. arrowAtStart.Set(FALSE); arrowAtStartI := arrowAtStart.Get();
  32. NEW(arrowAtEnd, NIL, StrArrowEnd, StrArrowEndDescription); properties.Add(arrowAtEnd);
  33. arrowAtEnd.Set(TRUE); arrowAtEndI := arrowAtEnd.Get();
  34. PropertyChanged(SELF, start); (* recompute bounding box *)
  35. END Init;
  36. PROCEDURE PropertyChanged*(sender, property : ANY);
  37. VAR dx, dy :LONGINT; rect, rect0: WMRectangles.Rectangle;
  38. BEGIN
  39. IF (property = color) THEN colorI := color.Get(); Invalidate;
  40. ELSIF (property = isVertical) THEN isVerticalI := isVertical.Get(); Invalidate;
  41. ELSIF (property = start) OR (property = end) THEN
  42. rect0:=bounds.Get(); startI := start.Get(); endI := end.Get();
  43. rect:=WMRectangles.MakeRect(MIN(startI.x,endI.x)+rect0.l-5, MIN(startI.y,endI.y)+rect0.t-5, MAX(startI.x, endI.x)+rect0.l+5, MAX(startI.y,endI.y)+rect0.t+5); (* add border for arrowhead display *)
  44. dx:=rect.l - rect0.l;
  45. dy:=rect.t - rect0.t;
  46. startI.x:=startI.x-dx; endI.x:=endI.x-dx;
  47. startI.y:=startI.y-dy; endI.y:=endI.y-dy;
  48. IF ~WMRectangles.IsEqual(rect,rect0) THEN bounds.Set(rect); END;
  49. start.Set(startI);
  50. end.Set(endI);
  51. Invalidate;
  52. ELSIF (property = arrowAtStart) THEN arrowAtStartI := arrowAtStart.Get(); Invalidate;
  53. ELSIF (property = arrowAtEnd) THEN arrowAtEndI := arrowAtEnd.Get(); Invalidate;
  54. ELSE PropertyChanged^(sender, property);
  55. END;
  56. END PropertyChanged;
  57. (* position a line line in parent coordinates*)
  58. PROCEDURE Set*(x0,y0, x1, y1: LONGINT);
  59. VAR rect:WMRectangles.Rectangle; changed:BOOLEAN;
  60. BEGIN
  61. rect:=bounds.Get();
  62. changed:=FALSE;
  63. IF (x0 # rect.l + startI.x) OR (y0#rect.t+startI.y) THEN start.SetCoordinate(x0-rect.l, y0-rect.t); changed:=TRUE END;
  64. IF (x1 # rect.l + endI.x) OR (y1#rect.t+endI.y) THEN end.SetCoordinate(x1-rect.l, y1-rect.t); changed:=TRUE END;
  65. IF changed THEN PropertyChanged(SELF, start); PropertyChanged(SELF, end) END;
  66. END Set;
  67. (** Return if the line is hit at (x, y) in parent coordinates *)
  68. PROCEDURE IsHit*(x, y: LONGINT): BOOLEAN;
  69. VAR r: WMRectangles.Rectangle; X0,Y0, X1,Y1: LONGINT; hit:BOOLEAN;
  70. BEGIN
  71. IF ~visible.Get() THEN hit:= FALSE
  72. ELSE
  73. r:=GetClientRect();
  74. X0:=startI.x+r.l; Y0:=startI.y+r.t;
  75. X1:=endI.x+r.l; Y1:=endI.y+r.t;
  76. IF X0=X1 THEN hit:=WMRectangles.PointInRect(x, y, r) & (2>ABS(x-X0))
  77. ELSIF Y0=Y1 THEN hit:=WMRectangles.PointInRect(x, y, r) & (2>ABS(y-Y0))
  78. ELSE hit:= WMRectangles.PointInRect(x, y, r) & (2>ABS((y-Y0) - ((x-X0)*(Y1-Y0)/(X1-X0))))
  79. END;
  80. END;
  81. RETURN hit;
  82. END IsHit;
  83. PROCEDURE SetArrowheads*(arrows:SET);
  84. BEGIN
  85. IF (0 IN arrows)#arrowAtStartI THEN arrowAtStart.Set(0 IN arrows); PropertyChanged(SELF, arrowAtStart); END;
  86. IF (1 IN arrows)#arrowAtEndI THEN arrowAtEnd.Set(1 IN arrows); PropertyChanged(SELF, arrowAtEnd); END;
  87. END SetArrowheads;
  88. PROCEDURE DrawBackground*(canvas : WMGraphics.Canvas);
  89. CONST pi=3.1516; headscale= 0.25;
  90. VAR alpha: REAL;
  91. dx,dy: LONGINT;
  92. size:LONGINT; head: LONGREAL;
  93. BEGIN
  94. DrawBackground^(canvas);
  95. IF (colorI # 0) THEN
  96. dx:=endI.x-startI.x; dy:=endI.y-startI.y;
  97. alpha:=arctan2(dx,dy);
  98. size:= 40; (*! to do: parametrize arrow size *)
  99. head:=size * headscale (* + 2 *);
  100. canvas.Line(startI.x, startI.y, endI.x, endI.y, colorI, WMGraphics.ModeSrcOverDst);
  101. IF arrowAtEndI THEN
  102. canvas.Line(endI.x,endI.y, endI.x - ENTIER(0.5+head * Math.cos(alpha + pi/8)), endI.y - ENTIER(0.5+head * Math.sin(alpha + pi/8)), colorI, WMGraphics.ModeSrcOverDst);
  103. canvas.Line(endI.x,endI.y, endI.x - ENTIER(0.5+head * Math.cos(alpha - pi/8)), endI.y - ENTIER(0.5+head * Math.sin(alpha - pi/8)), colorI, WMGraphics.ModeSrcOverDst);
  104. END;
  105. IF arrowAtStartI THEN
  106. canvas.Line(startI.x,startI.y, startI.x + ENTIER(0.5+head * Math.cos(alpha + pi/8)), startI.y + ENTIER(0.5+head * Math.sin(alpha + pi/8)), colorI, WMGraphics.ModeSrcOverDst);
  107. canvas.Line(startI.x,startI.y, startI.x + ENTIER(0.5+head * Math.cos(alpha - pi/8)), startI.y + ENTIER(0.5+head * Math.sin(alpha - pi/8)), colorI, WMGraphics.ModeSrcOverDst);
  108. END
  109. END;
  110. END DrawBackground;
  111. END Line;
  112. TYPE
  113. Rectangle* = OBJECT(WMComponents.VisualComponent)
  114. VAR
  115. clBorder- : WMProperties.ColorProperty;
  116. clBorderI : WMGraphics.Color;
  117. PROCEDURE &Init*;
  118. BEGIN
  119. Init^;
  120. SetGenerator("WMShapes.GenRectangle");
  121. SetNameAsString(StrRectangle);
  122. NEW(clBorder, NIL, StrClBorder, StrClBorderDescription); properties.Add(clBorder);
  123. clBorder.Set(WMGraphics.Black); clBorderI := clBorder.Get();
  124. END Init;
  125. PROCEDURE PropertyChanged*(sender, property : ANY);
  126. BEGIN
  127. IF (property = clBorder) THEN
  128. clBorderI := clBorder.Get();
  129. Invalidate;
  130. ELSE
  131. PropertyChanged^(sender, property);
  132. END;
  133. END PropertyChanged;
  134. PROCEDURE DrawBackground*(canvas : WMGraphics.Canvas);
  135. VAR rect : WMRectangles.Rectangle;
  136. BEGIN
  137. DrawBackground^(canvas);
  138. IF (clBorderI # 0) THEN
  139. rect := GetClientRect();
  140. WMGraphicUtilities.DrawRect(canvas, rect, clBorderI, WMGraphics.ModeSrcOverDst);
  141. END;
  142. END DrawBackground;
  143. END Rectangle;
  144. TYPE
  145. Circle* = OBJECT(WMComponents.VisualComponent)
  146. VAR
  147. color : WMProperties.ColorProperty;
  148. colorI : WMGraphics.Color;
  149. PROCEDURE &Init*;
  150. BEGIN
  151. Init^;
  152. SetGenerator("WMShapes.GenCircle");
  153. SetNameAsString(StrCircle);
  154. NEW(color, NIL, Strings.NewString("Color"), Strings.NewString("Color")); properties.Add(color);
  155. color.Set(WMGraphics.Black); colorI := color.Get();
  156. END Init;
  157. PROCEDURE PropertyChanged*(sender, property : ANY);
  158. BEGIN
  159. IF (property = color) THEN
  160. colorI := color.Get();
  161. Invalidate;
  162. ELSE
  163. PropertyChanged^(sender, property);
  164. END;
  165. END PropertyChanged;
  166. PROCEDURE DrawBackground*(canvas : WMGraphics.Canvas);
  167. VAR rect : WMRectangles.Rectangle; radius : LONGINT;
  168. BEGIN
  169. DrawBackground^(canvas);
  170. IF (colorI # 0) THEN
  171. rect := bounds.Get();
  172. canvas.SetColor(colorI);
  173. radius := MIN((rect.r - rect.l) DIV 2, (rect.b - rect.t) DIV 2) - 1;
  174. WMGraphicUtilities.Circle(canvas, (rect.r - rect.l) DIV 2, (rect.b - rect.t) DIV 2, radius);
  175. END;
  176. END DrawBackground;
  177. END Circle;
  178. TYPE
  179. Ellipse* = OBJECT(WMComponents.VisualComponent)
  180. VAR
  181. color : WMProperties.ColorProperty;
  182. colorI : WMGraphics.Color;
  183. PROCEDURE &Init*;
  184. BEGIN
  185. Init^;
  186. SetGenerator("WMShapes.GenEllipse");
  187. SetNameAsString(StrEllipse);
  188. NEW(color, NIL, StrColor, StrColorDescription);
  189. color.Set(WMGraphics.Black); colorI := color.Get();
  190. END Init;
  191. PROCEDURE PropertyChanged*(sender, property : ANY);
  192. BEGIN
  193. IF (property = color) THEN
  194. colorI := color.Get();
  195. Invalidate;
  196. ELSE
  197. PropertyChanged^(sender, property);
  198. END;
  199. END PropertyChanged;
  200. PROCEDURE DrawBackground*(canvas : WMGraphics.Canvas);
  201. VAR rect : WMRectangles.Rectangle;
  202. BEGIN
  203. DrawBackground^(canvas);
  204. IF (colorI # 0) THEN
  205. rect := bounds.Get();
  206. canvas.SetColor(colorI);
  207. WMGraphicUtilities.Ellipse(canvas, (rect.r - rect.l) DIV 2, (rect.b - rect.t) DIV 2, (rect.r - rect.l) DIV 2 - 1, (rect.b - rect.t) DIV 2 - 1);
  208. END;
  209. END DrawBackground;
  210. END Ellipse;
  211. VAR
  212. StrLine, StrRectangle, StrCircle, StrEllipse : Strings.String;
  213. StrClBorder, StrClBorderDescription, StrColor, StrColorDescription, StrLineColorDescription,
  214. StrIsVertical, StrIsVerticalDescription,
  215. StrStart,StrEnd,StrArrowStart, StrArrowEnd,
  216. StrStartDescription, StrEndDescription, StrArrowStartDescription,StrArrowEndDescription: Strings.String;
  217. PROCEDURE GenLine*() : XML.Element;
  218. VAR line : Line;
  219. BEGIN
  220. NEW(line); RETURN line;
  221. END GenLine;
  222. PROCEDURE GenRectangle*() : XML.Element;
  223. VAR rectangle : Rectangle;
  224. BEGIN
  225. NEW(rectangle); RETURN rectangle;
  226. END GenRectangle;
  227. PROCEDURE GenCircle*() : XML.Element;
  228. VAR circle : Circle;
  229. BEGIN
  230. NEW(circle); RETURN circle;
  231. END GenCircle;
  232. PROCEDURE GenEllipse*() : XML.Element;
  233. VAR ellipse : Ellipse;
  234. BEGIN
  235. NEW(ellipse); RETURN ellipse;
  236. END GenEllipse;
  237. PROCEDURE InitStrings;
  238. BEGIN
  239. StrLine := Strings.NewString("Line");
  240. StrRectangle := Strings.NewString("StrRectangle");
  241. StrCircle := Strings.NewString("StrCircle");
  242. StrEllipse := Strings.NewString("StrEllipse");
  243. StrClBorder := Strings.NewString("ClBorder");
  244. StrClBorderDescription := Strings.NewString("Border color");
  245. StrColor := Strings.NewString("Color");
  246. StrColorDescription := Strings.NewString("Color");
  247. StrLineColorDescription := Strings.NewString("Color of line");
  248. StrStart := Strings.NewString("LineStart");
  249. StrStartDescription := Strings.NewString("start point of line");
  250. StrEnd := Strings.NewString("LineEnd");
  251. StrEndDescription := Strings.NewString("end point of line");
  252. StrArrowStart := Strings.NewString("ArrowAtStart");
  253. StrArrowStartDescription := Strings.NewString("arrows at start of line ?");
  254. StrArrowEnd := Strings.NewString("ArrowAtEnd");
  255. StrArrowEndDescription := Strings.NewString("arrows at end of line ?");
  256. StrIsVertical := Strings.NewString("IsVertical");
  257. StrIsVerticalDescription := Strings.NewString("Horizontal or vertical line?");
  258. END InitStrings;
  259. PROCEDURE arctan2(x,y: REAL): REAL; (*arctan in range 0..2pi*)
  260. BEGIN
  261. IF (x>0) & (y>=0) THEN RETURN Math.arctan(y/x)
  262. ELSIF (x>0) & (y<0) THEN RETURN Math.arctan(y/x)+2*Math.pi
  263. ELSIF x<0 THEN RETURN Math.arctan(y/x)+Math.pi
  264. ELSIF (x=0) & (y>0) THEN RETURN Math.pi/2
  265. ELSIF (x=0) & (y<0) THEN RETURN 3*Math.pi/2
  266. ELSE (*( x=0) & (y=0) *) RETURN 0 (*or RETURN NaN ?*)
  267. END
  268. END arctan2;
  269. BEGIN
  270. InitStrings;
  271. END WMShapes.
  272. System.FreeDownTo WMShapes ~