WMFigures.Mod 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179
  1. MODULE WMFigures; (** AUTHOR "Patrick Hunziker, with inspirations from staubesv, gadgets source"; PURPOSE "Geometric shapes"; *)
  2. IMPORT
  3. KernelLog, Streams, Math, Strings, XML, WMRectangles, WMGraphics, WMGraphicUtilities, WMProperties, WMComponents;
  4. CONST
  5. (* Figure.state *)
  6. Filled* = 0;
  7. Closed* = 1;
  8. EditPoints* = 2;
  9. Reshape*=3;
  10. Arrow*=4;
  11. PointSize = 6; (* size of the control points. Currently not related to Effects.gravity *)
  12. TYPE
  13. Point* = POINTER TO RECORD
  14. x, y : LONGINT;
  15. previous, next : Point;
  16. END;
  17. TYPE
  18. Figure* = OBJECT(WMComponents.VisualComponent)
  19. VAR
  20. width- : WMProperties.Int32Property; (* in pixels. should rather be real. lineWidth=0 means hairline. *)
  21. color-, clHover- : WMProperties.ColorProperty;
  22. closed-: WMProperties.BooleanProperty;
  23. filled-: WMProperties.BooleanProperty;
  24. reshape-: WMProperties.BooleanProperty;
  25. arrow-: WMProperties.BooleanProperty;
  26. points- : Point;
  27. nofPoints : LONGINT;
  28. hover, selectedPoint:WMProperties.Property;
  29. selectedLine:BOOLEAN;
  30. mouseOver:BOOLEAN;
  31. lastKeys, state : SET;
  32. oldx,oldy:LONGINT;
  33. PArray: WMProperties.PropertyArray; (* PArray#NIL !*)
  34. PROCEDURE &Init*;
  35. BEGIN
  36. Init^;
  37. SetNameAsString(StrFigure);
  38. NEW(width, PrototypeWidth, NIL, NIL); properties.Add(width);
  39. NEW(color, PrototypeColor, NIL, NIL); properties.Add(color);
  40. NEW(reshape, PrototypeReshape, NIL, NIL); properties.Add(reshape);
  41. NEW(arrow, PrototypeArrow, NIL, NIL); properties.Add(arrow);
  42. NEW(closed, PrototypeClosed, NIL, NIL); properties.Add(closed);
  43. NEW(filled, PrototypeFilled, NIL, NIL); properties.Add(filled);
  44. NEW(clHover, PrototypeclHover, NIL, NIL); properties.Add(clHover);
  45. state := {};
  46. IF closed.Get() THEN INCL(state,Closed) END;
  47. IF filled.Get() THEN INCL(state,Filled) END;
  48. IF reshape.Get() THEN INCL(state,Reshape) END;
  49. IF arrow.Get() THEN INCL(state,Arrow) END;
  50. points := NIL;
  51. nofPoints := 0;
  52. hover := NIL;
  53. selectedPoint := NIL;
  54. lastKeys := {};
  55. NEW(PArray,0);
  56. END Init;
  57. PROCEDURE AddPoint*(x, y : LONGINT); (* in stable parent coordinates (because coordinates relative to the figure bound change with normalization) *)
  58. VAR
  59. s: ARRAY 16 OF CHAR;
  60. pp:WMProperties.PointProperty;
  61. (*a: XML.Attribute;*)
  62. BEGIN
  63. Strings.IntToStr(nofPoints,s);
  64. Strings.Concat("Point",s,s);
  65. INC(nofPoints);
  66. NEW(pp,NIL,Strings.NewString(s),NIL);
  67. pp.SetCoordinate(x,y);
  68. selectedPoint:=pp;
  69. Acquire;
  70. properties.Add(pp);
  71. Release;
  72. END AddPoint;
  73. (* x,y in Figure coordinates, which may change when adding new points etc. Remember that PointProperty is in (stable) parent coordinates*)
  74. PROCEDURE MovePoint*(point: WMProperties.Property; fx, fy: LONGINT);
  75. BEGIN
  76. point(WMProperties.PointProperty).SetCoordinate(fx+bounds.GetLeft(), fy+bounds.GetTop());
  77. END MovePoint;
  78. (* remove point coordinates are currently in figure coordinates, in contrast to AddPoint() *)
  79. PROCEDURE RemovePoint*(fx, fy : LONGINT);
  80. VAR
  81. pp:WMProperties.Property;
  82. BEGIN
  83. pp:=ThisPoint(fx,fy);
  84. IF pp#NIL THEN
  85. Acquire;
  86. properties.Remove(pp);
  87. Release;
  88. END;
  89. END RemovePoint;
  90. (** Return point located at mouse position fx. fy, which are in Figure coordinates that change, e.g. when points are added. (NIL if no point at location). *)
  91. PROCEDURE ThisPoint*(fx, fy : LONGINT): WMProperties.Property;
  92. VAR i:LONGINT; px,py: LONGINT;
  93. BEGIN
  94. i:=0;
  95. WHILE i<LEN(PArray) DO
  96. IF (PArray[i]#NIL)&(PArray[i] IS WMProperties.PointProperty) THEN
  97. PArray[i](WMProperties.PointProperty).GetCoordinate(px,py);
  98. IF Invicinity(fx, fy, px-bounds.GetLeft(), py-bounds.GetTop()) THEN RETURN PArray[i] END;
  99. END;
  100. INC(i);
  101. END;
  102. RETURN NIL;
  103. END ThisPoint;
  104. (** Return point Nr 'index' . Negative numbers are counted top-down. *)
  105. PROCEDURE IndexedPoint*(index : LONGINT): WMProperties.Property;
  106. VAR i,j:LONGINT;
  107. BEGIN
  108. i:=0; j:=0;
  109. IF index >=0 THEN
  110. WHILE i<LEN(PArray) DO
  111. IF (PArray[i]#NIL)&(PArray[i] IS WMProperties.PointProperty) THEN
  112. IF j=index THEN RETURN PArray[i]
  113. ELSE INC(j)
  114. END;
  115. END;
  116. INC(i);
  117. END;
  118. ELSE
  119. i:=LEN(PArray)-1; j:=1; index:=-index;
  120. WHILE i>=0 DO
  121. IF (PArray[i]#NIL)&(PArray[i] IS WMProperties.PointProperty) THEN
  122. IF j=index THEN RETURN PArray[i]
  123. ELSE INC(j)
  124. END;
  125. END;
  126. DEC(i);
  127. END;
  128. END;
  129. RETURN NIL;
  130. END IndexedPoint;
  131. PROCEDURE MoveFigure*(dx,dy:LONGINT);
  132. VAR i,x,y:LONGINT;
  133. BEGIN
  134. Acquire;
  135. IF PArray=NIL THEN PArray:=properties.Enumerate(); END; (*?redundant*)
  136. IF PArray=NIL THEN RETURN END;
  137. FOR i:=0 TO LEN(PArray)-1 DO
  138. IF (PArray[i]#NIL) & (PArray[i] IS WMProperties.PointProperty) THEN
  139. PArray[i](WMProperties.PointProperty).GetCoordinate(x, y);
  140. PArray[i](WMProperties.PointProperty).SetCoordinate(x+dx, y+dy);
  141. END;
  142. END;
  143. Release;
  144. END MoveFigure;
  145. (* fx,fy in Figure coordinate system, NOT in parent coordinates*)
  146. PROCEDURE PointerDown*(fx, fy : LONGINT; keys : SET);
  147. BEGIN
  148. lastKeys := keys;
  149. IF (0 IN keys) THEN
  150. oldx:=fx+bounds.GetLeft(); oldy:=fy+bounds.GetTop();
  151. selectedPoint := ThisPoint(fx, fy);
  152. IF (selectedPoint # NIL) THEN (*Invalidate*) ELSE selectedLine:=TRUE; mouseOver:=FALSE END;
  153. Invalidate;
  154. ELSIF (EditPoints IN state) & (keys={1}) THEN
  155. AddPoint(bounds.GetLeft()+fx, bounds.GetTop()+fy);
  156. PropertyChanged(SELF, properties);
  157. END;
  158. END PointerDown;
  159. PROCEDURE PointerUp*(fx, fy : LONGINT; keys : SET);
  160. VAR dx,dy,bt,bl:LONGINT;
  161. BEGIN
  162. bl:=bounds.GetLeft();
  163. bt:=bounds.GetTop();
  164. IF Reshape IN state THEN
  165. IF keys*{0,1}#{} THEN
  166. IF (selectedPoint # NIL) THEN
  167. MovePoint(selectedPoint, fx, fy);
  168. ELSE
  169. dx:=fx+bl-oldx; oldx:=fx+bl;
  170. dy:=fy+bt-oldy; oldy:=fy+bt;
  171. MoveFigure(dx, dy);
  172. END;
  173. ELSIF (EditPoints IN state) THEN
  174. IF (lastKeys={2}) & (keys#{2}) THEN
  175. RemovePoint(fx,fy);
  176. PropertyChanged(SELF, properties);
  177. ELSE
  178. END;
  179. (*ELSIF (2 IN lastKeys) & ~(2 IN keys) THEN
  180. IF Reshape IN state THEN EXCL(state, Reshape); ELSE INCL(state, Reshape); END;
  181. Invalidate;
  182. *)
  183. ELSE
  184. END;
  185. ELSE(* PointerUp^(x, y, keys);*)
  186. END;
  187. selectedLine:=FALSE;
  188. selectedPoint:=NIL;
  189. END PointerUp;
  190. PROCEDURE PointerMove*(fx, fy : LONGINT; keys : SET);
  191. VAR dx,dy,bl,bt:LONGINT; pp:WMProperties.Property;
  192. BEGIN
  193. IF (Reshape IN state) & (keys*{0,1}#{}) & (selectedPoint # NIL) THEN
  194. MovePoint(selectedPoint, fx, fy);
  195. ELSIF (Reshape IN state) & (keys={0}) & selectedLine THEN
  196. bl:=bounds.GetLeft();
  197. bt:=bounds.GetTop();
  198. dx:=fx+bl-oldx; oldx:=fx+bl;
  199. dy:=fy+bt-oldy; oldy:=fy+bt;
  200. MoveFigure(dx, dy);
  201. (*ELSIF (Reshape IN state) & ~(0 IN keys) THEN
  202. mouseOver:=IsHit(fx,fy);
  203. *)
  204. ELSE
  205. (*bl:=bounds.GetLeft();
  206. bt:=bounds.GetTop();
  207. PointerMove^(fx+bl, fy+bt, keys);
  208. pp := ThisPoint(fx+bl, fy+bt);
  209. hover:=pp;
  210. mouseOver := ~mouseOver; *)
  211. END;
  212. END PointerMove;
  213. (* Is X, Y somewhere inside the polygon defined by p ? *)
  214. PROCEDURE Inside*(X, Y: LONGINT): BOOLEAN;(*Prototype*)
  215. END Inside;
  216. (** Return if the line is hit at (x, y) in parent coordinates *)
  217. PROCEDURE IsHit*(x, y: LONGINT): BOOLEAN; (*Prototype*)
  218. END IsHit;
  219. PROCEDURE PropertyChanged*(sender, property : ANY);
  220. BEGIN
  221. IF (property = color) THEN Invalidate;
  222. ELSIF (property = width) THEN Invalidate;
  223. ELSIF (property = clHover) THEN Invalidate;
  224. ELSIF (property = closed) THEN IF closed.Get() THEN INCL(state,Closed) ELSE EXCL(state,Closed); END; Invalidate;
  225. ELSIF (property = filled) THEN IF filled.Get() THEN INCL(state,Filled) ELSE EXCL(state,Filled); END; Invalidate;
  226. ELSIF (property = reshape) THEN IF reshape.Get() THEN INCL(state,Reshape) ELSE EXCL(state,Reshape); END; Invalidate;
  227. ELSIF (property = arrow) THEN IF arrow.Get() THEN INCL(state,Arrow) ELSE EXCL(state,Arrow); END; Invalidate;
  228. ELSIF (property IS WMProperties.PointProperty) OR (property=properties) THEN
  229. RecachePoints;
  230. Normalize;
  231. Resized;(*implied Invalidate*)
  232. ELSE PropertyChanged^(sender, property);
  233. END;
  234. END PropertyChanged;
  235. PROCEDURE AddDisplayPoint(x, y : LONGINT);
  236. VAR point, p : Point;
  237. BEGIN
  238. Acquire;
  239. NEW(point); point.x := x; point.y := y; point.previous := NIL; point.next := NIL;
  240. IF (points = NIL) THEN points := point;
  241. ELSE
  242. p := points;
  243. WHILE (p.next # NIL) DO p := p.next; END;
  244. p.next := point; point.previous := p;
  245. END;
  246. INC(nofPoints);
  247. Release;
  248. END AddDisplayPoint;
  249. PROCEDURE MoveDisplayPoints(dx, dy : LONGINT);
  250. VAR p : Point;
  251. BEGIN
  252. Acquire;
  253. p := points;
  254. WHILE (p # NIL) DO p.x := p.x + dx; p.y := p.y + dy; p := p.next; END;
  255. Release;
  256. END MoveDisplayPoints;
  257. PROCEDURE Normalize;
  258. VAR p:Point; oldrect, rect: WMRectangles.Rectangle; dx,dy:LONGINT;
  259. BEGIN
  260. Acquire;
  261. rect.l := MAX(LONGINT); rect.t := MAX(LONGINT); rect.r := MIN(LONGINT); rect.b := MIN(LONGINT);
  262. p:=points;
  263. WHILE p#NIL DO (* adapt display point coordinates to new bounds *)
  264. rect.l:=MIN(rect.l, p.x-PointSize DIV 2 -1); rect.r:=MAX(rect.r, p.x+PointSize DIV 2+1); (*compute bounds*)
  265. rect.t:=MIN(rect.t, p.y-PointSize DIV 2 -1); rect.b:=MAX(rect.b, p.y+PointSize DIV 2+1);
  266. p:=p.next;
  267. END;
  268. p:=points;
  269. WHILE p#NIL DO (* adapt display point coordinates to new bounds *)
  270. p.x:=p.x-rect.l;
  271. p.y:=p.y-rect.t;
  272. p:=p.next;
  273. END;
  274. bounds.Set(rect);
  275. Release;
  276. END Normalize;
  277. PROCEDURE Scale;
  278. END Scale;
  279. PROCEDURE GetBoundingBox() : WMRectangles.Rectangle; (*! will be eliminated*)
  280. VAR rect : WMRectangles.Rectangle; i:LONGINT;
  281. BEGIN
  282. rect.l := MAX(LONGINT); rect.t := MAX(LONGINT);
  283. rect.r := MIN(LONGINT); rect.b := MIN(LONGINT);
  284. IF PArray#NIL THEN PArray:=properties.Enumerate(); END;
  285. IF PArray=NIL THEN RETURN rect END;
  286. FOR i:=0 TO LEN(PArray)-1 DO
  287. IF PArray[i] IS WMProperties.PointProperty THEN
  288. rect.l:=MIN(rect.l, PArray[i](WMProperties.PointProperty).GetX());
  289. rect.r:=MAX(rect.r, PArray[i](WMProperties.PointProperty).GetX());
  290. rect.t:=MIN(rect.t, PArray[i](WMProperties.PointProperty).GetY());
  291. rect.b:=MAX(rect.b, PArray[i](WMProperties.PointProperty).GetY());
  292. END;
  293. END;
  294. RETURN rect;
  295. END GetBoundingBox;
  296. PROCEDURE RecacheProperties*;
  297. BEGIN (* called by sequencer -> hierarchy is locked, see WMVisualComponentSkeleton.Mod *)
  298. IF closed.Get() THEN INCL(state,Closed) ELSE EXCL(state,Closed) END;
  299. IF filled.Get() THEN INCL(state,Filled); ELSE EXCL(state, Filled) END;
  300. IF reshape.Get() THEN INCL(state,Reshape) ELSE EXCL(state, Reshape) END;
  301. IF arrow.Get() THEN INCL(state,Arrow) ELSE EXCL(state, Arrow) END;
  302. RecacheProperties^;
  303. RecachePoints;
  304. Normalize;
  305. Resized;(* implied Invalidate - that is redundant*)
  306. END RecacheProperties;
  307. PROCEDURE RecachePoints; (*build point list in parent coordinates*)
  308. VAR p:Point; i,x,y:LONGINT;
  309. BEGIN
  310. Acquire;
  311. PArray:=properties.Enumerate();
  312. points:=NIL; p:=NIL; nofPoints:=0;
  313. FOR i:=0 TO LEN(PArray)-1 DO
  314. IF (PArray[i]#NIL)&(PArray[i] IS WMProperties.PointProperty) THEN
  315. PArray[i](WMProperties.PointProperty).GetCoordinate(x,y);
  316. AddDisplayPoint(x, y);
  317. END;
  318. END;
  319. Release;
  320. END RecachePoints;
  321. PROCEDURE DrawDisplayPoint(canvas : WMGraphics.Canvas; pp: WMProperties.PointProperty);
  322. VAR rect : WMRectangles.Rectangle; color, x,y,fx,fy : LONGINT;
  323. BEGIN
  324. ASSERT(pp # NIL);
  325. IF (pp = selectedPoint) THEN color := WMGraphics.Yellow;
  326. ELSIF (pp = hover) THEN color := WMGraphics.Blue;
  327. ELSE color := WMGraphics.White;
  328. END;
  329. pp.GetCoordinate(x,y);
  330. fx:=x-bounds.GetLeft();
  331. fy:=y-bounds.GetTop();
  332. rect := WMRectangles.MakeRect(fx- PointSize DIV 2, fy - PointSize DIV 2, fx + PointSize DIV 2, fy + PointSize DIV 2);
  333. canvas.Fill(rect, WMGraphics.White, WMGraphics.ModeSrcOverDst);
  334. WMGraphicUtilities.DrawRect(canvas, rect, WMGraphics.Black, WMGraphics.ModeSrcOverDst);
  335. END DrawDisplayPoint;
  336. PROCEDURE DrawForeground*(canvas : WMGraphics.Canvas);
  337. VAR a: BOOLEAN; i:LONGINT;
  338. BEGIN
  339. DrawForeground^(canvas);
  340. a:=arrow.Get();
  341. IF reshape.Get() THEN
  342. FOR i:=0 TO LEN(PArray)-1 DO
  343. IF (PArray[i]#NIL)&(PArray[i] IS WMProperties.PointProperty) THEN
  344. DrawDisplayPoint(canvas, PArray[i](WMProperties.PointProperty));
  345. END;
  346. END;
  347. END;
  348. END DrawForeground;
  349. PROCEDURE DrawArrow*(canvas : WMGraphics.Canvas; p0,p1: Point);
  350. CONST pi=3.1516;
  351. VAR alpha: REAL;
  352. head: LONGREAL;
  353. col: WMGraphics.Color;
  354. BEGIN
  355. alpha:=arctan2(p1.x-p0.x, p1.y-p0.y);
  356. head:=MAX( 4, 0.05 * MAX(ABS(p1.x-p0.x), ABS(p1.y-p0.y))); (*avoid sqrt for performance reasons*)
  357. col:=color.Get();
  358. canvas.Line(p1.x,p1.y, p1.x - ENTIER(0.5+head * Math.cos(alpha + pi/8)), p1.y - ENTIER(0.5+head * Math.sin(alpha + pi/8)), col, WMGraphics.ModeSrcOverDst);
  359. canvas.Line(p1.x,p1.y, p1.x - ENTIER(0.5+head * Math.cos(alpha - pi/8)), p1.y - ENTIER(0.5+head * Math.sin(alpha - pi/8)), col, WMGraphics.ModeSrcOverDst);
  360. END DrawArrow;
  361. END Figure;
  362. TYPE
  363. PointArray = POINTER TO ARRAY OF WMGraphics.Point2d;
  364. Line* = OBJECT(Figure)
  365. VAR
  366. pointArray : PointArray; (* {pointArray # NIL} *)
  367. PROCEDURE &Init*;
  368. BEGIN
  369. Init^;
  370. SetGenerator("WMFigures.GenLine");
  371. SetNameAsString(StrLine);
  372. INCL(state, EditPoints);
  373. RecachePoints;
  374. NEW(pointArray, nofPoints);
  375. END Init;
  376. PROCEDURE Initialize*;
  377. VAR pp:WMProperties.PointProperty;
  378. BEGIN
  379. RecachePoints;
  380. IF nofPoints=0 THEN (* prototype*)
  381. Acquire;
  382. NEW(pp,NIL,Strings.NewString("Point0"),NIL); pp.SetCoordinate(5,20); properties.Add(pp); INC(nofPoints);
  383. NEW(pp,NIL,Strings.NewString("Point1"),NIL); pp.SetCoordinate(20,0); properties.Add(pp); INC(nofPoints);
  384. NEW(pp,NIL,Strings.NewString("Point2"),NIL); pp.SetCoordinate(20,20); properties.Add(pp); INC(nofPoints);
  385. NEW(pp,NIL,Strings.NewString("Point3"),NIL); pp.SetCoordinate(30,30); properties.Add(pp); INC(nofPoints);
  386. Release;
  387. RecachePoints;
  388. END;
  389. Normalize;
  390. Initialize^;
  391. END Initialize;
  392. PROCEDURE Scale;
  393. VAR p : Point; bounds, box : WMRectangles.Rectangle; oldWidth, oldHeight, newWidth, newHeight, n : LONGINT;
  394. BEGIN
  395. Acquire;
  396. bounds := SELF.bounds.Get();
  397. box := GetBoundingBox();
  398. oldWidth := box.r - box.l;
  399. oldHeight := box.b - box.t;
  400. n := (PointSize DIV 2) + (width.Get() DIV 2) + 1;
  401. newWidth := bounds.r - bounds.l - 2*n;
  402. newHeight := bounds.b - bounds.t - 2*n;
  403. IF (oldWidth # 0) & (oldHeight # 0) THEN
  404. p := points;
  405. WHILE (p # NIL) DO
  406. p.x := (p.x - box.l) * newWidth DIV oldWidth + box.l;
  407. p.y := (p.y - box.t) * newHeight DIV oldHeight + box.t;
  408. p := p.next;
  409. END;
  410. END;
  411. Release;
  412. END Scale;
  413. (* Is X, Y somewhere inside the polygon defined by p ? *)
  414. PROCEDURE Inside*(X, Y: LONGINT): BOOLEAN;
  415. VAR c: LONGINT; p, q: Point;
  416. BEGIN
  417. c := 0;
  418. IF (points # NIL) THEN
  419. p := points; q:=p.next;
  420. WHILE q#NIL DO
  421. IF Intersect(X, Y, p.x, p.y, q.x, q.y) THEN INC(c) END;
  422. p:=q; q:=q.next;
  423. END;
  424. IF (nofPoints > 1) & Intersect(X, Y, p.x, p.y, points.x, points.y) THEN INC(c) END;
  425. END;
  426. RETURN ODD(c);
  427. END Inside;
  428. PROCEDURE IsHit*(mx, my: LONGINT): BOOLEAN;
  429. VAR p, q: Point; i : LONGINT;
  430. BEGIN
  431. IF (points = NIL) OR (points.next = NIL) THEN RETURN FALSE; END;
  432. IF ~ (Reshape IN state) THEN RETURN FALSE
  433. ELSIF Filled IN state THEN
  434. IF Inside(mx, my) THEN RETURN TRUE END;
  435. END;
  436. p := points; q := points.next;
  437. WHILE (q # NIL) DO
  438. IF InLineVicinity(mx, my, p.x, p.y, q.x, q.y) THEN RETURN TRUE END;
  439. p:=q; q:=q.next; INC(i);
  440. END;
  441. IF (Closed IN state) OR (Filled IN state) THEN
  442. IF InLineVicinity(mx, my, p.x, p.y, points.x, points.y) THEN RETURN TRUE END;
  443. END;
  444. RETURN FALSE
  445. END IsHit;
  446. PROCEDURE DrawBackground*(canvas : WMGraphics.Canvas);
  447. VAR p, plast : Point; i : LONGINT;
  448. BEGIN
  449. canvas.SetLineWidth(width.Get());
  450. DrawBackground^(canvas);
  451. IF (nofPoints # LEN(pointArray)) THEN NEW(pointArray, nofPoints); END;
  452. p := points; i := 0;
  453. WHILE (p # NIL) DO
  454. pointArray[i].x := p.x;
  455. pointArray[i].y := p.y;
  456. INC(i);
  457. plast:=p; p := p.next;
  458. END;
  459. IF (Arrow IN state) & (plast#NIL) THEN DrawArrow(canvas, plast.previous, plast) END;
  460. IF Filled IN state THEN canvas.FillPolygonFlat(pointArray^, nofPoints, color.Get(), WMGraphics.ModeSrcOverDst);
  461. ELSE canvas.PolyLine(pointArray^, nofPoints, closed.Get(), color.Get(), WMGraphics.ModeSrcOverDst);
  462. END;
  463. END DrawBackground;
  464. END Line;
  465. TYPE
  466. Circle* = OBJECT(Figure)
  467. PROCEDURE &Init*;
  468. BEGIN
  469. Init^;
  470. SetGenerator("WMFigures.GenCircle");
  471. SetNameAsString(StrCircle);
  472. EXCL(state, EditPoints);
  473. END Init;
  474. PROCEDURE Initialize*;
  475. VAR pp: WMProperties.PointProperty;
  476. BEGIN
  477. RecachePoints;
  478. IF nofPoints=0 THEN (* prototype*)
  479. NEW(pp,NIL,Strings.NewString("Point0"),NIL); pp.SetCoordinate(15,15); properties.Add(pp); INC(nofPoints);
  480. NEW(pp,NIL,Strings.NewString("Point1"),NIL); pp.SetCoordinate(30,30); properties.Add(pp); INC(nofPoints);
  481. bounds.Set(WMRectangles.MakeRect(0,0,40,40));
  482. RecachePoints;
  483. END;
  484. Normalize;
  485. Initialize^;
  486. END Initialize;
  487. PROCEDURE Normalize;
  488. VAR p:Point; rect: WMRectangles.Rectangle; r,n:LONGINT;
  489. BEGIN
  490. rect.l := MAX(LONGINT); rect.t := MAX(LONGINT); rect.r := MIN(LONGINT); rect.b := MIN(LONGINT);
  491. p:=points;
  492. r := Distance(p.x, p.y, p.next.x, p.next.y);
  493. n := r + (PointSize DIV 2) + 1;
  494. (* adapt display point coordinates to new bounds *)
  495. rect.l:=p.x-n; rect.r:=p.x+n; (*compute bounds*)
  496. rect.t:=p.y-n; rect.b:=p.y+n;
  497. p:=points;
  498. WHILE p#NIL DO (* adapt display point coordinates to new bounds *)
  499. p.x:=p.x-rect.l; p.y:=p.y-rect.t;
  500. p:=p.next;
  501. END;
  502. bounds.Set(rect);
  503. END Normalize;
  504. PROCEDURE IsHit*(mx, my: LONGINT): BOOLEAN;
  505. VAR radius0, radius: LONGINT;
  506. BEGIN
  507. IF ~ (Reshape IN state) THEN RETURN FALSE END;
  508. radius0:= Distance(points.x, points.y, points.next.x,points.next.y);
  509. radius:=Distance(mx,my, points.x,points.y);
  510. IF (Filled IN state) THEN RETURN radius<=radius0 (*inside circle*)
  511. ELSIF radius < gravity THEN RETURN TRUE (*center point hit*)
  512. ELSIF ABS(radius - radius0)<gravity THEN RETURN TRUE (*boundary line hit*)
  513. ELSE RETURN FALSE
  514. END;
  515. END IsHit;
  516. PROCEDURE DrawBackground*(canvas : WMGraphics.Canvas);
  517. VAR p, q : Point;
  518. BEGIN
  519. DrawBackground^(canvas);
  520. RecachePoints;
  521. Normalize;
  522. IF (points=NIL) OR (points.next=NIL) THEN RETURN END;
  523. p := points;
  524. q := points.next;
  525. canvas.SetColor(color.Get());
  526. IF (*Filled IN state*) FALSE THEN (*canvas.FillPolygonFlat(pointArray^, nofPoints, color.Get(), WMGraphics.ModeSrcOverDst); *)(*! to be done: draw filled circle*)
  527. ELSE WMGraphicUtilities.Circle(canvas, p.x, p.y, Distance(p.x, p.y, q.x, q.y));
  528. END;
  529. IF (Arrow IN state) THEN DrawArrow(canvas, p,q); END;
  530. END DrawBackground;
  531. END Circle;
  532. TYPE
  533. Rectangle* = OBJECT(Figure)
  534. PROCEDURE &Init*;
  535. BEGIN
  536. Init^;
  537. SetGenerator("WMFigures.GenRectangle");
  538. SetNameAsString(StrRectangle);
  539. EXCL(state, EditPoints);
  540. END Init;
  541. PROCEDURE Initialize*;
  542. VAR pp: WMProperties.PointProperty;
  543. BEGIN
  544. RecachePoints;
  545. IF nofPoints=0 THEN (* prototype*)
  546. NEW(pp,NIL,Strings.NewString("Point0"),NIL); pp.SetCoordinate(10,10); properties.Add(pp); INC(nofPoints);
  547. NEW(pp,NIL,Strings.NewString("Point1"),NIL); pp.SetCoordinate(30,30); properties.Add(pp); INC(nofPoints);
  548. bounds.Set(WMRectangles.MakeRect(0,0,40,40));
  549. RecachePoints;
  550. END;
  551. Normalize;
  552. Initialize^;
  553. END Initialize;
  554. PROCEDURE IsHit*(mx, my: LONGINT): BOOLEAN;
  555. BEGIN
  556. IF ~ (Reshape IN state) THEN RETURN FALSE
  557. ELSIF Filled IN state THEN
  558. RETURN ((mx-points.x)*(mx-points.next.x) <=0 ) & ((my-points.y)*(my-points.next.y) <=0 ) (* simple "in-between" test *)
  559. ELSE RETURN
  560. InLineVicinity(mx, my, points.x, points.y, points.x, points.next.y) OR
  561. InLineVicinity(mx, my, points.x, points.y, points.next.x, points.y) OR
  562. InLineVicinity(mx, my, points.x, points.next.y, points.next.x, points.next.y) OR
  563. InLineVicinity(mx, my, points.next.x, points.y, points.next.x, points.next.y)
  564. END;
  565. END IsHit;
  566. PROCEDURE DrawBackground*(canvas : WMGraphics.Canvas);
  567. VAR p, q : Point; rect : WMRectangles.Rectangle;
  568. BEGIN
  569. DrawBackground^(canvas);
  570. RecachePoints;
  571. Normalize;
  572. IF (points=NIL) OR (points.next=NIL) THEN RETURN END;
  573. p := points;
  574. q := points.next;
  575. rect.l := MIN(p.x, q.x);
  576. rect.r := MAX(p.x, q.x);
  577. rect.t := MIN(p.y, q.y);
  578. rect.b := MAX(p.y, q.y);
  579. IF (*Filled IN state *) FALSE THEN (*canvas.FillPolygonFlat(pointArray^, nofPoints, color.Get(), WMGraphics.ModeSrcOverDst);*) (*! to be done *)
  580. ELSE WMGraphicUtilities.DrawRect(canvas, rect, color.Get(), WMGraphics.ModeSrcOverDst);
  581. END;
  582. END DrawBackground;
  583. END Rectangle;
  584. TYPE
  585. Spline* = OBJECT(Figure)
  586. VAR
  587. pointArray : ARRAY 2048 OF WMGraphics.Point2d;
  588. nSegments:LONGINT;
  589. PROCEDURE &Init*;
  590. BEGIN
  591. Init^;
  592. SetGenerator("WMFigures.GenSpline");
  593. SetNameAsString(StrSpline);
  594. INCL(state, EditPoints);
  595. RecachePoints;
  596. END Init;
  597. PROCEDURE Initialize*;
  598. VAR pp:WMProperties.PointProperty;
  599. BEGIN
  600. IF nofPoints=0 THEN (* default *)
  601. Acquire;
  602. NEW(pp,NIL,Strings.NewString("Point0"),NIL); pp.SetCoordinate(5,20); properties.Add(pp); INC(nofPoints);
  603. NEW(pp,NIL,Strings.NewString("Point1"),NIL); pp.SetCoordinate(20,0); properties.Add(pp); INC(nofPoints);
  604. NEW(pp,NIL,Strings.NewString("Point2"),NIL); pp.SetCoordinate(20,20); properties.Add(pp); INC(nofPoints);
  605. NEW(pp,NIL,Strings.NewString("Point3"),NIL); pp.SetCoordinate(30,30); properties.Add(pp); INC(nofPoints);
  606. Release;
  607. END;
  608. RecachePoints;
  609. SplineToPoly(points, Closed IN state, pointArray, nSegments);
  610. Normalize;
  611. Initialize^;
  612. END Initialize;
  613. (* Is X, Y somewhere inside the polygon defined by p ? *) (*! to be implemented for pointarray*)
  614. PROCEDURE Inside*(X, Y: LONGINT): BOOLEAN;
  615. VAR c: LONGINT; p,q: Point;
  616. BEGIN
  617. c := 0;
  618. IF (points # NIL) THEN
  619. p := points; q := p.next;
  620. WHILE q#NIL DO
  621. IF Intersect(X, Y, p.x, p.y, q.x, q.y) THEN INC(c) END;
  622. p:=q; q:=q.next;
  623. END;
  624. IF (nofPoints > 1) & Intersect(X, Y, p.x, p.y, points.x, points.y) THEN INC(c) END;
  625. END;
  626. RETURN ODD(c);
  627. END Inside;
  628. PROCEDURE IsHit*(mx, my: LONGINT): BOOLEAN;
  629. VAR p, q: Point; i : LONGINT;
  630. BEGIN
  631. IF (points = NIL) OR (points.next = NIL) THEN RETURN FALSE; END;
  632. IF Filled IN state THEN
  633. IF Inside(mx, my) THEN RETURN TRUE END;
  634. END;
  635. FOR i:=0 TO nSegments-1 DO
  636. IF (ABS(mx-pointArray[i].x)<gravity) & (ABS(my-pointArray[i].y)<gravity) THEN RETURN TRUE END; (* here only testing segment points, can be refined*)
  637. END;
  638. (*!the following code is for line hiting above. does not work so well for spline yet*)
  639. (*p := points; q := points.next;
  640. WHILE (q # NIL) DO
  641. IF InLineVicinity(mx, my, p.x, p.y, q.x, q.y) THEN RETURN TRUE END;
  642. p:=q; q:=q.next; INC(i);
  643. END;
  644. IF (Closed IN state) OR (Filled IN state) THEN
  645. IF InLineVicinity(mx, my, p.x, p.y, points.x, points.y) THEN RETURN TRUE END;
  646. END;*)
  647. RETURN FALSE
  648. END IsHit;
  649. (*PROCEDURE PropertyChanged*(sender, property : ANY);
  650. BEGIN
  651. IF (property=closed) THEN
  652. IF closed.Get() THEN INCL(state,Closed) ELSE EXCL(state,Closed); END;
  653. RecachePoints;
  654. SplineToPoly(points, Closed IN state, pointArray, nSegments);
  655. Normalize;
  656. Resized; (*implied Invalidate; *)
  657. ELSIF (property=properties) OR (property IS WMProperties.PointProperty) THEN
  658. RecachePoints;
  659. SplineToPoly(points, Closed IN state, pointArray, nSegments);
  660. Normalize;
  661. Resized;
  662. (*should call PropertyChanged^ in some cases here ?*)
  663. ELSE
  664. RecachePoints;
  665. SplineToPoly(points, Closed IN state, pointArray, nSegments);
  666. Normalize;
  667. Resized;
  668. PropertyChanged^(sender,property);
  669. END;
  670. END PropertyChanged;*)
  671. PROCEDURE PropertyChanged*(sender, property : ANY);
  672. BEGIN
  673. RecacheProperties;
  674. SplineToPoly(points, Closed IN state, pointArray, nSegments);
  675. Normalize;
  676. Resized;(* implied Invalidate - that is redundant*)
  677. IF ~(property IS WMProperties.PointProperty) & ~(property=closed) THEN
  678. PropertyChanged^(sender,property);
  679. END;
  680. END PropertyChanged;
  681. PROCEDURE Normalize;
  682. VAR i:LONGINT; oldrect, rect:WMRectangles.Rectangle; p:Point;
  683. BEGIN
  684. oldrect:=bounds.Get();
  685. rect.l := MAX(LONGINT); rect.t := MAX(LONGINT); rect.r := MIN(LONGINT); rect.b := MIN(LONGINT);
  686. FOR i:=0 TO nSegments-1 DO
  687. rect.l:=MIN(rect.l, pointArray[i].x-PointSize DIV 2 -1); rect.r:=MAX(rect.r, pointArray[i].x+PointSize DIV 2+1); (*compute bounds*)
  688. rect.t:=MIN(rect.t, pointArray[i].y-PointSize DIV 2 -1); rect.b:=MAX(rect.b, pointArray[i].y+PointSize DIV 2+1);
  689. END;
  690. FOR i:=0 TO nSegments-1 DO
  691. pointArray[i].x:=pointArray[i].x-rect.l;
  692. pointArray[i].y:=pointArray[i].y-rect.t;
  693. END;
  694. p:=points;
  695. WHILE p#NIL DO (* adapt display point coordinates to new bounds *)
  696. p.x:=p.x-rect.l; p.y:=p.y-rect.t;
  697. p:=p.next;
  698. END;
  699. bounds.Set(rect);
  700. END Normalize;
  701. PROCEDURE DrawBackground*(canvas : WMGraphics.Canvas);
  702. VAR p,pa: Point; col: WMGraphics.Color; d:REAL;
  703. BEGIN
  704. DrawBackground^(canvas);
  705. IF mouseOver THEN col:=clHover.Get() ELSE col:=color.Get() END;
  706. IF Filled IN state THEN
  707. canvas.FillPolygonFlat(pointArray, nSegments, col, WMGraphics.ModeSrcOverDst);
  708. ELSE
  709. canvas.PolyLine(pointArray, nSegments, closed.Get(), col, WMGraphics.ModeSrcOverDst);
  710. END;
  711. IF arrow.Get() (*(Arrow IN state)*) & (p#NIL) THEN
  712. (*
  713. d:=Math.sqrt( (p.x-p.previous.x)*(p.x-p.previous.x) + (p.y-p.previous.y)*(p.y-p.previous.y));
  714. d:=10/d;
  715. NEW(pa);
  716. pa.x:= p.x - ENTIER(d*(p.x-p.previous.x));
  717. pa.y:= p.y - ENTIER(d*(p.y-p.previous.y));
  718. DrawArrow(canvas, pa, p);
  719. *)
  720. DrawArrow(canvas, p.previous, p)
  721. END;
  722. END DrawBackground;
  723. END Spline;
  724. VAR
  725. log: Streams.Writer;
  726. (* Size of gravity spot used for "snapping" the cursor *)
  727. gravity : LONGINT;
  728. PrototypeWidth : WMProperties.Int32Property;
  729. PrototypeColor, PrototypeclHover : WMProperties.ColorProperty;
  730. PrototypeClosed: WMProperties.BooleanProperty;
  731. PrototypeFilled: WMProperties.BooleanProperty;
  732. PrototypeReshape: WMProperties.BooleanProperty;
  733. PrototypeArrow: WMProperties.BooleanProperty;
  734. StrFigure, StrLine, StrCircle, StrRectangle, StrSpline : Strings.String;
  735. PROCEDURE arctan2(x,y: REAL): REAL; (*arctan in range 0..2pi*)
  736. BEGIN
  737. IF (x>0) & (y>=0) THEN RETURN Math.arctan(y/x)
  738. ELSIF (x>0) & (y<0) THEN RETURN Math.arctan(y/x)+2*Math.pi
  739. ELSIF x<0 THEN RETURN Math.arctan(y/x)+Math.pi
  740. ELSIF (x=0) & (y>0) THEN RETURN Math.pi/2
  741. ELSIF (x=0) & (y<0) THEN RETURN 3*Math.pi/2
  742. ELSE (*( x=0) & (y=0) *) RETURN 0 (*or RETURN NaN ?*)
  743. END
  744. END arctan2;
  745. (* start of Rege code *)
  746. PROCEDURE MakePoly(CONST RX, RY, RXstrich, RYstrich, RS: ARRAY OF REAL; n: LONGINT; VAR points : ARRAY OF WMGraphics.Point2d; VAR k: LONGINT);
  747. TYPE
  748. Polynom = RECORD A, B, C, D: REAL END;
  749. VAR
  750. i, cs, smax, k1: LONGINT; px, py: Polynom;
  751. x, dx1, dx2, dx3, y, dy1, dy2, dy3: REAL; L, B, R, T,dW : LONGINT;
  752. PROCEDURE GetPolynom((* VAR *) y1, y2, y1s, y2s: REAL; VAR p: Polynom);
  753. VAR dx1, dyx: REAL;
  754. BEGIN
  755. IF RS[i] # RS[i+1] THEN dx1 := 1.0/(RS[i + 1] - RS[i]) ELSE dx1 := 1.0 END;
  756. dyx := (y2 - y1)*dx1;
  757. p.A := dx1*dx1*(-2.0*dyx + y1s + y2s);
  758. p.B := dx1*(3.0*dyx - 2.0*y1s - y2s);
  759. p.C := y1s;
  760. p.D := y1
  761. END GetPolynom;
  762. BEGIN
  763. points[0].x := SHORT(ENTIER(RX[1])); points[0].y := SHORT(ENTIER(RY[1]));
  764. L := MAX(LONGINT); B := MAX(LONGINT); R := MIN(LONGINT); T := MIN(LONGINT);
  765. i := 1; WHILE i <= n DO
  766. L := MIN(L,SHORT(ENTIER(RX[i]))); B := MIN(B,SHORT(ENTIER(RY[i])));
  767. R := MAX(R,SHORT(ENTIER(RX[i]))); T := MAX(T,SHORT(ENTIER(RY[i])));
  768. INC(i);
  769. END;
  770. dW := MAX(1,MIN((MAX(R-L ,T-B) * 3 DIV n DIV 20),4));
  771. i := 1; k := 1;
  772. WHILE i < n DO
  773. GetPolynom(RX[i], RX[i+1], RXstrich[i], RXstrich[i+1], px);
  774. x := px.D;
  775. dx1 := px.A + px.B + px.C;
  776. dx3 := 6.0*px.A;
  777. dx2 := dx3 + 2.0*px.B;
  778. GetPolynom(RY[i], RY[i+1], RYstrich[i], RYstrich[i+1], py);
  779. y := py.D;
  780. dy1 := py.A + py.B + py.C;
  781. dy3 := 6.0*py.A;
  782. dy2 := dy3 + 2.0*py.B;
  783. smax := SHORT(ENTIER(RS[i+1]-RS[i]));
  784. cs := 0;
  785. WHILE cs <= smax DO
  786. points[k].x := SHORT(ENTIER(x)); points[k].y := SHORT(ENTIER(y));
  787. k1 := k-1;
  788. IF (ABS(points[k].x - points[k1].x) > dW) OR (ABS(points[k].y - points[k1].y) > dW) THEN INC(k) END;
  789. x := x + dx1; y := y + dy1;
  790. dx1 := dx1 + dx2; dy1 := dy1 + dy2;
  791. dx2 := dx2 + dx3; dy2 := dy2 + dy3;
  792. INC(cs);
  793. END;
  794. INC(i);
  795. END; (* FOR i *)
  796. points[k].x := SHORT(ENTIER(RX[n])); points[k].y := SHORT(ENTIER(RY[n])); INC(k);
  797. END MakePoly;
  798. PROCEDURE SplineToPoly(c: Point; closed: BOOLEAN; VAR points : ARRAY OF WMGraphics.Point2d; VAR k: LONGINT);
  799. TYPE
  800. RealVect = ARRAY 256 OF REAL;
  801. VAR
  802. n, i: LONGINT; RS, RX, RY ,RXstrich, RYstrich : RealVect; dx, dy: REAL;
  803. helpR: REAL;
  804. PROCEDURE NatSplineDerivates(VAR x, y, d: ARRAY OF REAL; n: LONGINT);
  805. VAR i: LONGINT; d1, d2: REAL; a, b, c: RealVect;
  806. PROCEDURE SolveTriDiag(VAR a, b, c: ARRAY OF REAL; n: LONGINT; VAR y: ARRAY OF REAL);
  807. VAR i: LONGINT; t: REAL;
  808. BEGIN i := 1;
  809. WHILE i < n DO t := a[i]; c[i] := c[i]/t; helpR := c[i]*b[i]; a[i+1] := a[i+1] - helpR; INC(i); END;
  810. i := 2;
  811. WHILE i <= n DO helpR := c[i-1]*y[i-1]; y[i] := y[i] - helpR; INC(i); END;
  812. t := a[n]; y[n] := y[n]/t; i := n-1;
  813. WHILE i > 0 DO t := y[i+1]; helpR :=y[i] - b[i]*t; y[i] := helpR/a[i]; DEC(i) END
  814. END SolveTriDiag;
  815. BEGIN (* NatSplineDerivates *)
  816. IF x[1] # x[2] THEN b[1] := 1.0/(x[2] - x[1]); ELSE b[1] := 1.0 END;
  817. a[1] := 2.0*b[1]; c[1] := b[1];
  818. d1 := (y[2] - y[1])*3.0*b[1]*b[1];
  819. d[1] := d1;
  820. i :=2;
  821. WHILE i < n DO
  822. IF x[i] # x[i+1] THEN b[i] := 1.0 /(x[i+1] - x[i]) ELSE b[i] := 1.0 END;
  823. a[i] := 2.0*(c[i-1] + b[i]); c[i] := b[i];
  824. d2 := (y[i+1] - y[i])*3.0*b[i]*b[i];
  825. d[i] := d1 + d2; d1 := d2;
  826. INC(i);
  827. END;
  828. a[n] := 2.0*b[n-1]; d[n] := d1;
  829. SolveTriDiag(a, b, c, n, d)
  830. END NatSplineDerivates;
  831. PROCEDURE ClSplineDerivates(VAR x, y, d: ARRAY OF REAL; n: LONGINT);
  832. VAR i: LONGINT; hn1, dn1, d1, d2: REAL; a, b, c, u: RealVect;
  833. PROCEDURE SolveTriDiag2(VAR a, b, c: ARRAY OF REAL; n:LONGINT; VAR y1, y2: ARRAY OF REAL);
  834. VAR i: LONGINT; t: REAL;
  835. BEGIN
  836. i := 1;
  837. WHILE i < n DO
  838. t := a[i]; c[i] := c[i]/t;
  839. helpR := c[i]*b[i]; a[i+1] := a[i+1] - helpR;
  840. INC(i)
  841. END;
  842. i :=2;
  843. WHILE i <= n DO
  844. helpR := c[i-1]*y1[i-1]; y1[i] := y1[i] - helpR;
  845. helpR := c[i-1]*y2[i-1]; y2[i] := y2[i] - helpR;
  846. INC(i);
  847. END;
  848. t := a[n]; y1[n] := y1[n]/t; t := a[n]; y2[n] := y2[n]/t;
  849. i := n-1;
  850. WHILE i > 0 DO
  851. t := y1[i+1]; helpR := y1[i] - b[i]* t; y1[i] := helpR/a[i];
  852. t := y2[i+1]; helpR :=y2[i] - b[i]*t; y2[i] := helpR/a[i];
  853. DEC(i)
  854. END
  855. END SolveTriDiag2;
  856. BEGIN (* ClSplineDerivates *)
  857. hn1 := 1.0/(x[n] - x[n-1]);
  858. dn1 := (y[n] - y[n-1])*3.0*hn1*hn1;
  859. IF x[2] # x[1] THEN
  860. b[1] := 1.0/(x[2] - x[1]);
  861. ELSE
  862. b[1] := 0
  863. END;
  864. a[1] := hn1 + 2.0*b[1];
  865. c[1] := b[1];
  866. d1 := (y[2] - y[1])*3.0*b[1]*b[1];
  867. d[1] := dn1 + d1;
  868. u[1] := 1.0;
  869. i := 2;
  870. WHILE i < n-1 DO
  871. IF x[i+1] # x[i] THEN b[i] := 1.0/(x[i+1] - x[i]) ELSE b[i] := 0 END;
  872. a[i] := 2.0*(c[i-1] + b[i]);
  873. c[i] := b[i];
  874. d2 := (y[i+1] - y[i])*3.0*b[i]*b[i];
  875. d[i] := d1 + d2;
  876. d1 := d2;
  877. u[i] := 0.0;
  878. INC(i)
  879. END;
  880. a[n-1] := 2.0*b[n-2] + hn1;
  881. d[n-1] := d1 + dn1;
  882. u[n-1] := 1.0;
  883. SolveTriDiag2(a, b, c, n-1, u, d);
  884. helpR := u[1] + u[n-1] + x[n] - x[n-1];
  885. d1 := (d[1] + d[n-1])/helpR;
  886. i := 1;
  887. WHILE i < n DO
  888. d[i] := d[i] - d1*u[i];
  889. INC(i)
  890. END;
  891. d[n] := d[1]
  892. END ClSplineDerivates;
  893. BEGIN
  894. IF c=NIL THEN k:=0; RETURN END;
  895. n := 0; WHILE c # NIL DO RX[n+1] := c.x ; RY[n+1] := c.y; INC(n); c := c.next END;
  896. IF closed THEN RX[n+1] := RX[1]; RY[n+1] := RY[1]; INC(n) ; END;
  897. RS[1] := 0.0; i := 2;
  898. WHILE i <= n DO
  899. dx := RX[i] - RX[i-1]; dy := RY[i] - RY[i-1];
  900. RS[i] := RS[i-1] + Math.sqrt(dx*dx + dy*dy);
  901. INC(i);
  902. END;
  903. IF ~closed THEN
  904. NatSplineDerivates(RS, RX, RXstrich, n);
  905. NatSplineDerivates(RS, RY, RYstrich, n);
  906. ELSE
  907. ClSplineDerivates(RS, RX, RXstrich, n);
  908. ClSplineDerivates(RS, RY, RYstrich, n)
  909. END;
  910. MakePoly(RX, RY, RXstrich, RYstrich, RS, n, points, k);
  911. END SplineToPoly;
  912. (* end of Rege code *)
  913. (** Returns TRUE if mx, my is within gravity pixels from X, Y. *)
  914. PROCEDURE Invicinity(mx, my, X, Y: LONGINT): BOOLEAN;
  915. BEGIN RETURN (mx - X) * (mx - X) + (my - Y) * (my - Y) < gravity * gravity
  916. END Invicinity;
  917. (** Returns TRUE if mx, my is within gravity pixels of the line from X, Y to X1, Y1. *)
  918. PROCEDURE InLineVicinity(mx, my, X, Y, X1, Y1: LONGINT): BOOLEAN;
  919. VAR w, h, pw, ph, det,len : LONGINT;
  920. PROCEDURE Between(x, a, b: LONGINT): BOOLEAN;
  921. VAR min, max: LONGINT;
  922. BEGIN
  923. min := MIN(a, b); max := MAX(a, b);
  924. RETURN (min - gravity <= x) & (x <= max + gravity);
  925. END Between;
  926. BEGIN
  927. IF ABS(X - X1) > gravity THEN
  928. IF ABS(Y - Y1) > gravity THEN
  929. IF Invicinity(mx, my,X, Y) OR Invicinity(mx, my,X1, Y1) THEN RETURN TRUE END;
  930. pw := mx - X; ph := my - Y; w := X1 -X; h := Y1 - Y;
  931. det := pw * h - ph * w; len := w * w + h * h;
  932. RETURN Between(mx, X, X1) & Between(my, Y, Y1) & (det / len * det < gravity * gravity)
  933. ELSE
  934. RETURN Between(mx, X, X1) & (ABS(my - Y) < gravity)
  935. END
  936. ELSE
  937. RETURN Between(my, Y, Y1) & (ABS(mx - X) < gravity)
  938. END
  939. END InLineVicinity;
  940. PROCEDURE Intersect(X, Y, x0,y0,x1,y1 : LONGINT) : BOOLEAN;
  941. BEGIN
  942. IF ((Y >= y0) & (Y < y1)) OR ((Y >= y1) & (Y < y0)) THEN
  943. IF y1 > y0 THEN RETURN x0 + (Y - y0) * (x1 -x0) DIV (y1 - y0) - X >= 0
  944. ELSIF y1 < y0 THEN RETURN x0 + (Y - y0) * (x0 -x1) DIV (y0 - y1) - X >= 0
  945. ELSE RETURN (x0 > X) OR (x1 > X)
  946. END
  947. ELSE RETURN FALSE
  948. END
  949. END Intersect;
  950. PROCEDURE Distance(x, y, x0, y0: LONGINT): LONGINT;
  951. VAR dx, dy: LONGINT;
  952. BEGIN dx := x - x0; dy := y - y0;
  953. RETURN ENTIER(Math.sqrt(dx * dx + dy * dy))
  954. END Distance;
  955. PROCEDURE GenLine*() : XML.Element; (* needs AddPoint(); AddPoint(), before a prototype becomes visible *)
  956. VAR line : Line;
  957. BEGIN
  958. NEW(line); RETURN line;
  959. END GenLine;
  960. PROCEDURE GenCircle*() : XML.Element;
  961. VAR circle : Circle;
  962. BEGIN
  963. NEW(circle); RETURN circle;
  964. END GenCircle;
  965. PROCEDURE GenRectangle*() : XML.Element;
  966. VAR rectangle : Rectangle;
  967. BEGIN
  968. NEW(rectangle); RETURN rectangle;
  969. END GenRectangle;
  970. PROCEDURE GenSpline*() : XML.Element;
  971. VAR spline : Spline;
  972. BEGIN
  973. NEW(spline); RETURN spline;
  974. END GenSpline;
  975. PROCEDURE InitPrototypes;
  976. BEGIN
  977. NEW(PrototypeWidth, NIL, Strings.NewString("width"), Strings.NewString("Width of stroke")); PrototypeWidth.Set(1);
  978. NEW(PrototypeColor, NIL, Strings.NewString("color"), Strings.NewString("Color")); PrototypeColor.Set(WMGraphics.Gray);
  979. NEW(PrototypeclHover, NIL, Strings.NewString("clHover"), Strings.NewString("Color HOver")); PrototypeclHover.Set(WMGraphics.Yellow);
  980. NEW(PrototypeClosed, NIL, Strings.NewString("closed"), Strings.NewString("Figure is closed")); PrototypeClosed.Set(FALSE);
  981. NEW(PrototypeFilled, NIL, Strings.NewString("filled"), Strings.NewString("Figure is filled")); PrototypeFilled.Set(FALSE);
  982. NEW(PrototypeReshape, NIL, Strings.NewString("reshape"), Strings.NewString("Control Points can be individually moved")); PrototypeReshape.Set(TRUE);
  983. NEW(PrototypeArrow, NIL, Strings.NewString("arrow"), Strings.NewString("Draw arrow at end of line")); PrototypeArrow.Set(FALSE);
  984. END InitPrototypes;
  985. PROCEDURE InitStrings;
  986. BEGIN
  987. StrFigure := Strings.NewString("Figure");
  988. StrLine := Strings.NewString("Line");
  989. StrCircle := Strings.NewString("Circle");
  990. StrRectangle := Strings.NewString("Rectangle");
  991. StrSpline := Strings.NewString("Spline");
  992. END InitStrings;
  993. BEGIN
  994. gravity := 6;
  995. InitStrings;
  996. InitPrototypes;
  997. Streams.OpenWriter(log, KernelLog.Send);
  998. END WMFigures.
  999. System.FreeDownTo WMFigures ~
  1000. ----------------
  1001. Example commands for interactive figures (left click for editing and moving; middle clicking for adding points, right-left interclick for deleting points).
  1002. WMComponents.Open FigureExample.Cwd ~
  1003. ComponentViewer.Open WMFigures.GenSpline ~
  1004. ComponentViewer.Open WMStandardComponents.GenPanel ~
  1005. ------------------------
  1006. Example application for programmable figures:
  1007. MODULE TestFigures;
  1008. IMPORT WMFigures, WMWindowManager, WMGraphics, WMComponents, WMStandardComponents;
  1009. PROCEDURE Do*;
  1010. VAR
  1011. r:WMFigures.Rectangle;
  1012. s:WMFigures.Spline;
  1013. l:WMFigures.Line;
  1014. c:WMFigures.Circle;
  1015. window: WMComponents.FormWindow;
  1016. background: WMStandardComponents.Panel;
  1017. BEGIN
  1018. NEW(window, 400,400, FALSE);
  1019. NEW(background);
  1020. background.bounds.SetExtents(400,400);
  1021. background.fillColor.Set(WMGraphics.Green);
  1022. NEW(l); l.AddPoint(10,20); l.AddPoint(100,200); l.AddPoint(80, 20);
  1023. background.AddContent(l);
  1024. NEW(r); r.AddPoint(30,40); r.AddPoint(120,200);
  1025. background.AddContent(r)
  1026. NEW(c); c.AddPoint(100,100); c.AddPoint(150,150);
  1027. background.AddContent(c);
  1028. NEW(s); (*spline with default points*)
  1029. background.AddContent(s);
  1030. s.color.Set(WMGraphics.Blue);
  1031. NEW(r); r.AddPoint(70,80); r.AddPoint(110,110);
  1032. r.reshape.Set(FALSE); (* non-reshapable *)
  1033. r.color.Set(WMGraphics.Blue);
  1034. background.AddContent(r);
  1035. window.SetContent(background); (* calls window.form.Reset(NIL,NIL) implicitly *)
  1036. WMWindowManager.AddWindow(window, 200,200);
  1037. INCL(window.flags, WMWindowManager.FlagStorable); (* allow interactive storage of window through context menu *)
  1038. (* add content after window insertion to display *)
  1039. NEW(l); l.AddPoint(20,30); l.AddPoint(110,210); l.AddPoint(90, 40);
  1040. background.AddContent(l);
  1041. END Do;
  1042. PROCEDURE Do1*;
  1043. VAR
  1044. r:WMFigures.Rectangle;
  1045. s:WMFigures.Spline;
  1046. l:WMFigures.Line;
  1047. window: WMComponents.FormWindow;
  1048. BEGIN
  1049. NEW(window, 400,400, FALSE);
  1050. WMWindowManager.AddWindow(window, 200,200);
  1051. INCL(window.flags, WMWindowManager.FlagStorable);
  1052. NEW(l); l.AddPoint(10,20); l.AddPoint(100,200); l.AddPoint(80, 20);
  1053. window.SetContent(l);
  1054. END Do1;
  1055. END TestFigures.
  1056. TestFigures.Do
  1057. TestFigures.Do1
  1058. System.FreeDownTo WMFigures TestFigures ~