Rotate Function for Component Drawed with Canvas Lazarus - freepascal

I need a function in Lazarus that with it I can rotate some components instead of rewrite them (swapping x with y and viceversa)
for example:
i am using a class developed by another user
with this class i had to define the name of the component with properties and after a procedure NameComponent.Draw
the motedi engine draw the components reading this Draw procedure.
i save the components on a file sch (with also wires junction resistors etc)
when i load a circuit ...my software creates components (also wires junction) with saved properties (include points of connection where wire is attached)
wires has also node property
my software creates the netlist etc
procedure SW.Draw;
var angle:extended;
begin
if (self.Orientamento = '') or (self.Orientamento = '1') then
begin
v2d.setpen(pssolid,1,clBlue);
v2d.SetBrush(clwhite);
v2d.Line(x,y+30,x+100,y+30);
v2d.line(x,y+30,x,y+120);
v2d.line(x,y+120,x+100,y+120);
v2d.line(x+100,y+30,x+100,y+120);
v2d.line(x+20,y+30,x+20,y+10); // nc-
v2d.line(x+80,y+30,x+80,y+10); // nc+
v2d.line(x+20,y+120,x+20,y+140); // n-
v2d.line(x+80,y+120,x+80,y+140); // n+
// interruttore dentro
v2d.line(x+20,y+120,x+20,y+80); // a
v2d.line(x+80,y+120,x+80,y+80); // b
v2d.line(x+20,y+80,x+40,y+80); // c
v2d.line(x+60,y+80,x+80,y+80); // d
if (self.State = 'OFF') then
begin
v2d.setpen(pssolid,2,clGreen);
v2d.line(x+40,y+80,x+63,y+72); // e
end
else
begin
v2d.setpen(pssolid,2,clGreen);
v2d.line(x+40,y+80,x+60,y+80);
end;
v2d.setpen(pssolid,1,clBlue);
v2d.SetText(clRed, 6,'', true);
v2d.Texto(X+82, Y +110, 'N+');
v2d.Texto(X+74, Y +35, 'NC+');
v2d.SetText(clBlack, 6,'', true);
v2d.Texto(X+22, Y +110, 'N-');
v2d.Texto(X+15, Y +35, 'NC-');
v2d.SetText(clBlack, 8,'', true);
v2d.Texto(x+104,y+35, self.Nome);
v2d.Texto(x+104,y+50, self.VT);
v2d.Texto(x+104,y+65, self.VH);
v2d.Texto(x+104,y+80, self.RON);
v2d.Texto(x+104,y+95, self.ROFF);
end;
if (self.Orientamento = '2') then
begin
v2d.setpen(pssolid,1,clBlue);
v2d.SetBrush(clwhite);
v2d.line(x+30,y,x+30,y+100);
v2d.line(x+30,y,x+120,y);
v2d.line(x+120,y,x+120,y+100);
v2d.line(x+30,y+100,x+120,y+100);
v2d.line(x+30,y+20,x+10,y+20); // nc-
v2d.line(x+30,y+80,x+10,y+80); // nc +
v2d.line(x+120,y+20, x+140,y+20); // n-
v2d.line(x+120,y+80,x+140,y+80); // n+
v2d.line(x+120, y+20, x+80, y+20);
v2d.line(x+120,y+80,x+80,y+80);
v2d.line(x+80, y+20, x+80, y+40);
v2d.line(x+80, y+60, x+80, y+80);
if (self.State = 'OFF') then
begin
v2d.setpen(pssolid,2,clGreen);
// v2d.line(x+80, y+40, x+72, y+63);
v2d.line(x+80, y+60, x+72, y+37);
end
else
begin
v2d.setpen(pssolid,2,clGreen);
v2d.line(x+80,y+60, x+80, y+40);
// v2d.line(x+80, y+40, x+80, y+60);
end;
v2d.setpen(pssolid,1,clBlue);
v2d.SetText(clBlack, 6,'', true);
v2d.texto(x+110, y+82, 'N-');
v2d.texto(x+35, y+74, 'NC-');
v2d.SetText(clRed, 6,'', true);
v2d.Texto(x+110, y+22, 'N+');
v2d.Texto(x+35, y+15, 'NC+');
end;
inherited;
end;

In case you're asking for code, that's supports degrees instead of pre-defined values, here's an example, which uses Math PI to perform sectoral rotation of a point on 2-dimensional space:
function findCenter(Width, Height: Integer): TPoint;
begin
// Specify rotation center
Result := TPoint.Create(Width DIV 2, Height DIV 2);
end;
function Rotate(What, Center: TPoint; Angle: Double): TPoint; overload;
begin
if Angle > 0 then
begin
// Perform rotation
Result.X :=
ROUND(
Center.X + (What.X - Center.X)* COS(Angle)
- (What.Y - Center.Y)* SIN(Angle) );
Result.Y :=
ROUND(
Center.X + (What.X - Center.X)* SIN(Angle)
+ (What.Y - Center.Y)* COS(Angle) );
end
else
Result := TPoint.Create(What);
end;
function Rotate(What, Center: TPoint; Degrees: Integer): TPoint; overload;
var RotationAngle: Double;
begin
// Convert Degress to Radians
RotationAngle := Degrees * PI/180;
Result := Rotate(What, Center, RotationAngle);
end;
function Rotate(What: TPoint; Degrees: Integer): TPoint; overload;
var RotationAngle: Double;
begin
RotationAngle := Degrees * PI/180;
Result := Rotate(What, TPoint.Create(0,0), RotationAngle);
end;
Use the function Rotate() to rotate the location of your elements, for each of them you need to specify also a center of an element (Width and Height of parent element, divided by 2), information to get the center of element for text you can gather from WinAPI ( CalcTextWidth or CalcTextRect either )
The actual height of the text is the Font.Size - 1, so you can get Height value without external functions.
Note, that this probably won't work, if you're resizing your polygons, to resize them you need to calculate relations between every point or to split polygons to the triangles and do it for them (every n-heighted polygon can be splitted to triangles, the exception is ellipse, which has its own sizing technique - modifying the diameter ).
Also, be aware, that you must calculate new area sizes and positions offsets in order to keep final picture in field of view.

Related

Result of recursive function in Verilog

My math may not be serving me well. I've written a simple recursive function in Verilog to calculate value of log base 2. Log2(1000) should return 9.965 rounded to 10. But, my simulation shows that the function returns 9 as the final value of the recursive function. Any idea what I'm doing wrong?
module test;
real numBits;
function real log2 (input int X);
if (X == 1)
log2 = 0.0;
else begin
log2 = log2(X / 2) + 1.0;
$display($stime,,,"log2 = %0d",log2);
end
endfunction
initial begin
numBits = log2(1000);
$display($stime,,,"numBits = %f",numBits);
end
endmodule
Here's the EDA playground link that shows the code:
https://www.edaplayground.com/x/icx7
A couple of problems with your code. The first is the input to your function needs to be real. Then, it's never good to compare real numbers with equality do to rounding errors. Use X<=1 instead. And finally you should declare recursive functions with an automatic lifetime so that the arguments and the return values do not get overwritten.
function automatic real log2 (input real X);
if (X <= 1)
log2 = 0.0;
else begin
log2 = log2(X / 2) + 1.0;
$display($stime,,,"log2 = %0g",log2);
end
endfunction

Why are these lines of code in Pascal necessary?

The following example in Pascal was given in a book dedicated to the basics of programming. The function ReadLongint is supposed to check whether the input is 0-9 encoded in a char type. The function then returns true or false depending on the check results, as well as a variable for calculations, rendered into an integer via the operator ord().
Being a newbie I've had hard times figuring out how exactly this code works. But even the bigger mystery for me was the necessity of line 9.
'repeat
read(c);
position := position + 1;
until (c <> #32) and (c <> #10);'
I can see that it's a cycle which repeats itself if you input Space or Enter. However, I've checked the program without these lines, substituting it with a simple read(c); , and the program seems to work just fine. Can somebody please explain the role of this line in the example?
Here is the full program:
function ReadLongint(var check: longint): boolean;
var
c: char;
number: longint;
position: integer;
begin
number := 0;
position := 0;
repeat
read(c);
position := position + 1;
until (c <> #32) and (c <> #10);
while (c <> #32) and (c <> #10) do
begin
if (c < '0') or (c > '9') then
begin
writeln('Unexpected ''', c, ''' in position: ', position);
readln;
ReadLongint := false;
exit
end;
number := number * 10 + ord(c) - ord('0');
read(c);
position := position + 1
end;
check := number;
ReadLongint := true
end;
var
x, y: longint;
ok: boolean;
begin
repeat
write('Please type the first number: ');
ok := ReadLongint(x)
until ok = true;
repeat
write('Please type the second number: ');
ok := ReadLongint(y)
until ok = true;
writeln(x, ' times ', y, ' is ', x * y)
end.
Your readLongInt function wants to (partially) mimic the behavior of read/readLn. The behavior of read/readLn differs slightly if the destination variable is an integer (or real) or char value. The ISO Standard 7185 puts it like that:
c) If v is a variable-access possessing the integer-type (or subrange thereof), read(f, v) shall satisfy the following requirements. No component of s shall equal end‑of‑line. The components of r, if any, shall each, and (s ~t ~u).first shall not, equal either the char-type value space or end‑of‑line. […]
Translated to plain English: Leading spaces and newlines are ignored when interpreting a number (i. e. an integer [or real] variable as the destination). This is quite different to read/readLn in conjunction with a char‑variable where ' ' is a legal value, thus it is not “skipped”, you know.
The until (c <> #32) and (c <> #10) loop is trying to imitate the behavior of read(check) although the actually used read(c) merely reads a single char value, one after another.
PS: It always makes me cringe seeing ok = true. I hope you, unlike the author(s) of the textbook, knows that the = true is redundant, merely an identity.

Delphi: smooth collapse/expand form

Need your help (I'm stuck on searching for). I'm on Delphi Seattle, trying to make smooth resizing bottom of my form. In my case "resizing" is just a little collapse/expand like this:
How can I realize that?
I've tried use a TTimer:
procedure TForm1.Timer1Timer(Sender: TObject);
var
h, t: integer;
begin
t := Button10.Top + Button10.Height + 10; //slide TForm from/to this point
if t > h then
begin
h := h + 1;
Form1.Height := h;
end
else
begin
Timer1.Enabled := false;
end;
end;
... but it's looks very simple (no acceleration/deceleration) and works slow even with small interval.
There's no need to get complicated with TTimers. This will take care of both collapsing and expanding forms including the smoothness that you require.
The trick is to calculate each step by taking the Target Size - Current Height and div 3 at each iteration, which will both accelerate the initial collapse or expand, then decelerate as the form gets closer to its target size.
procedure TForm1.SmoothResizeFormTo(const ToSize: integer);
var
CurrentHeight: integer;
Step: integer;
begin
while Height <> ToSize do
begin
CurrentHeight := Form1.Height;
// this is the trick which both accelerates initially then
// decelerates as the form reaches its target size
Step := (ToSize - CurrentHeight) div 3;
// this allows for both collapse and expand by using Absolute
// calculated value
if (Step = 0) and (Abs(ToSize - CurrentHeight) > 0) then
begin
Step := ToSize - CurrentHeight;
Sleep(50); // adjust for smoothness
end;
if Step <> 0 then
begin
Height := Height + Step;
sleep(50); // adjust for smoothness
end;
end;
end;
procedure TForm1.btnCollapseClick(Sender: TObject);
begin
SmoothResizeFormTo(100);
end;
procedure TForm1.btnExpandClick(Sender: TObject);
begin
SmoothResizeFormTo(800);
end;

Delphi - for - loop

is there solution in Delphi 7 for deynamically creating for-loop cycles?
for example, i want to have a function that generates 1..n for-cycles
for example:
function generate_binary(var number_of_loops:integer):string
var x:word
begin
for x:=1 to number_of_loops do begin
for ... cycle 1
...
...
...
for ... cycle[x]
//code
end; //cycle n
end; //cycle[x]
end;
end;
is there some code for doing that? Or i must manually set every for.. cycles?
This is not direct answer to the topic question, but an example of particular problem solving. There are more effective methods of generation (especially for numbers, not for strings).
Look at this recursive code to generate all strings with predefined length and number of 1's. Note that output size will very large for big Len (number of combination C(N,K) is exponential function)
procedure Generate01Combination(Len, OnesLeft: Integer; s: string);
begin
if Len = 0 then
Memo1.Lines.Add(s)
else begin
if Len > OnesLeft then
Generate01Combination(Len - 1, OnesLeft, s + '0');
if OnesLeft > 0 then
Generate01Combination(Len - 1, OnesLeft - 1, s + '1');
end;
end;
begin
Generate01Combination(5, 2, '');
end;
outputs
00011
00101
00110
01001
01010
01100
10001
10010
10100
11000
Yes, you can kind of "generate loops" - if you enclose them into procedures and pass those procedures as procedure pointers.
But you said for ... cycle[1] - WHAT is that "..." ? Is it for I := 1 to 4 do cycle[1]() ? Or is it for I := cycle[1] to 10 do ; ? It all fits in!
So, well, - and yes, you indeed came with XYZ problem,- I will make an answer that formally fits, I think, but is hardly to help you with your own problem.
type TLoopProc = procedure(const LoopCount: integer);
procedure Loop1(const LoopCount: integer); var i: integer;
begin
for I := 1 to LoopCount do ShowMessage('Loop1 loop is burning!');
end;
procedure Loop2(const LoopCount: integer); var i: integer;
begin
for I := 1 to LoopCount do ShowMessage('Loop2 loop is burning!');
end;
procedure Loop3(const LoopCount: integer); var i: integer;
begin
for I := 1 to LoopCount do ShowMessage('Loop3 loop is burning!');
end;
procedure Loop4(const LoopCount: integer); var i: integer;
begin
for I := 1 to LoopCount do ShowMessage('Loop4 loop is burning!');
end;
var Loops: array[1..4] of TLoopProc;
function generate_binary(const number_of_loops:integer):string
var x,y:word;
begin
Result := '1234';
for x := 1 to number_of_loops do begin
for y := 1 to x do begin
Loops[y](x+y);
end;
end;
end;
BEGIN
Loops[1] := Loop1;
Loops[2] := Loop1;
Loops[3] := Loop1;
Loops[4] := Loop1;
generate_binary(4);
END.
See, formally that does call that ever increasing number of loops.
Those loops which bodies are contained inside their own procedures.
Not that I think you really can apply that to your real task.
But coming back to your real task, it is very very different.
You are given two numbers: N <= L with N being number of "1" to position in the string of Length L.
To me it looks a slightly hidden example of mathematical induction.
Can you position one single "1" in the string? I think you can.
But if you have all the strings with a single "1" - can you position the second "1" to the right of it? And then the 3rd "1" ?
So we would not search for the strings, we would search for ones' positions, sequences like 4-6-8-9-15-16-29-....
var results: iJclStringList;
// here it is just like TStringList, but interface-based, thus needs no `.Free`
type OnePositions = array of integer;
procedure Error;
begin
raise Exception.Create ('We badly screwed'); // or whatever you would make it do
end;
procedure StoreResult( const L: integer; const P1: OnePositions );
var R: string; i,Prev1,Next1: integer;
begin
R := StringOfChar( '0', L );
Prev1 := 0;
// if those Low/High functions are not yet implemented in Delphi7,
// you may run the loop from 0 to Prev(Length(P1))
// to go through all the P1 array elements
for i := Low(P1) to High(P1) do begin
Next1 := P1[i]; // position for next "1"
if Next1 > Length(R) then Error; // outside of string
if Prev1 >= Next1 then Error; // Next "1" is left of previous "1"
R[Next1] := '1';
Prev1 := Next1; // tracing what was the right-most "1" inserted
end;
Results.Add(R);
end;
// L - string length, thus maximum position of "1"
// StartAt - the leftmost (minimal) position of the 1st left "1" to place
// positions < StartAt already were taken
// Rest1s - how many "1" left to be placed (we still have to place)
procedure PositionRest(var Pos: OnePositions; const L, StartAt, Rest1s: integer);
var Max, idx, NextRest1s, i: integer;
begin
idx := Length(Pos) - Rest1s; // number of "1" we are setting now
NextRest1s := Rest1s - 1; // how many "1"s to be set by next calls
Max := L - NextRest1s; // rightmost part of string we have to leave free for next "1" to be placed
for i := StartAt to Max do begin
Pos[idx] := i; // placing our dear "1" here or there
if NextRest1s = 0 // did we maybe just positioned the last "1" ?
then StoreResult( L, Pos )
else PositionRest( Pos, L, i+1, NextRest1s);
end;
end;
procedure GenerateAll( const L,N: integer );
var Ones: OnePositions;
begin
results := JclStringList();
SetLength(Ones,N);
PositionRest(Ones, L, 1, N);
Memo1.Lines.Text := results.Text;
results := nil;
end;
var L: integer = 20; N: integer = 7;
GenerateAll( L,N );
Here are results at Phenom X3 710 2.6GHz CPU from CodeTyphon 5.60 and Delphi XE2: http://imgur.com/a/22B9b - 4 variants.
Example:
Only single core was used, would have to think how to make it parallelized;
Sources and Win32 EXE: http://rghost.ru/7lYwX2B4Y and http://rghost.ru/8RHmCKF4D
Project1 built by CT 5.60 and Project2 built by Delphi XE2
PS. Some general advices.
Whenever you can - use const parameters to functions. Only use var parameters when you know what it is and why you do need that. It is very rarely needed !
Do not use word for looping. Today CPU runs in 32 bits or 64 bits ( for Delphi 7 - only 32 bits ) - so use CPU-native type integer or cardinal, it would make it slightly easier for the hardware and 65535 maximum value for word might be too small sometimes.
Delphi 7 is good but old. Did you purchased it? Why to stick with it today? I think you better either purchase modern Delphi version or take free Lazarus/FPC suite (i'd stick with CodeTyphon distro, without controversial Orca);
PPS. I implemented OTL-based multithreading approach.
On a somewhat good note, I had to implement exactly dynamic number of loops approach.
As was expected MT-decoupling required extensive memory copying and multiple FastMM calls, which is uni-threaded by design, so multithreading was nothing but illusion and results were even worse than I expected -
still even that crippled MT-ing would provide to "show first 100 results while others are not being calculated yet.
I could enhance it here and there, getting rid of intermediate dynamics arrays (using lists and pointers instead) and switching to MT-oriented Heap Memory Manager, but that would clearly overflow the topic-started experience. It would be challenging to make most fast implementation, but since no one cares, then be it. Still this task was interesting synthetic example for pushing some OTL features and bounds.

FreePascal: how do I change the colour of a TPaint object on Mouseover

My Custom menu is coming along nicely and can detect when the mouse is within the boundaries of the link rectangles and respond to mouseup events. What I would now like for it to do is change the colours of the link rectangles when the user hovers the mouse within their boundaries. I've set colour properties this way before but manually and not dynamically.
Basically, nothing happens. Debugging shows that the mouse position routine is working perfectly but the rectangles stay the same colour.
I've created a simple array of ButtonStates and assigned them colours:
type
T_ButtonState = (bttn_off);
private
{ Private declarations }
bttnStatus : TOC_ButtonState;
const stateColor : array[T_ButtonState, false..true] of TColor = ((clDkGray, clGray));
And am now trying to manipulate the value of T_ButtonState so I can set the colour in my paint routine:
// User actions
procedure T_MenuPanel.MouseMove(Shift:TShiftState; X,Y:Integer);
var
loop : integer;
begin
for loop := 0 to High(MenuRects) do
begin
if PtInRect(MenuRects[loop], Point(X, Y)) then
bttnStatus := bttn_off;
end;
inherited;
end;
This is my drawing routine:
for count := 0 to fLinesText.Count - 1 do
begin
// Define y2
y2 := TextHeight(fLinesText.strings[count])*2;
// Draw the rectangle
itemR := Rect(x1, y1, x2, y2*(count+1));
Pen.color := clGray;
Brush.color := stateColor[bttn_off][bttnStatus = bttn_off]; // Nothing Happens!!!
Rectangle(itemR);
// Push rectangle info to array
MenuRects[count] := itemR;
// Draw the text
TextRect(itemR, x1+5, y1+5, fLinesText.strings[count]);
// inc y1 for positioning the next box
y1 := y1+y2;
end;
You're detecting where the mouse is when it moves, and you account for that detection when you draw. However, moving the mouse doesn't necessarily make your control redraw itself, so the movement isn't apparent in your control. When you detect mouse movement, signal to the control that it needs to be redrawn by calling its Invalidate method.
procedure T_MenuPanel.MouseMove(Shift:TShiftState; X,Y:Integer);
var
loop: integer;
begin
for loop := 0 to High(MenuRects) do begin
if PtInRect(MenuRects[loop], Point(X, Y)) then begin
bttnStatus := bttn_off;
// Ssgnal that we need repainting
Invalidate;
end;
end;
inherited;
end;
When the OS next has a chance, it will ask your control to repaint itself. That's usually sufficient, but if you want the visual update to be immediate, you can call Refresh instead of Invalidate.