I'm trying to write line intersection code to detect if two lines intersect.
the form i have stuff in is there are O objects that can have Lo(l subscript O) lines, each line has 2 points and each point has a x and a y.
this is the record format.
TPoint = record
x,y:integer;
end;
TLine = record
Point : array[0..1] of TPoint;
Color : Tcolor;
end;
TFill = record
Point : TPoint;
Color : Tcolor;
end;
TDObject = record
Lines : array of TLine;
Fills : array of TFill;
Rotation : integer;
Position : Tpoint;
BoundTop,Boundleft,Boundbottom,Boundright:integer;
end;
I call Code to iterate through each line combination of the two objects I wish to test for collision.
Function DoCollide(obj1,obj2:Tdobject):boolean;
var i,j:integer;
coll:boolean;
begin
coll:=false;
for i:=0 to length(obj1.lines) do
begin
for j:=0 to length(obj2.lines) do
begin
coll:=DoesIntersect(obj2.lines[i],obj2.lines[j])or coll;
end;
end;
result:=coll;
end;
each line test is done like so
Function DoesIntersect(Line1,Line2:Tline):boolean;
var
m1,m2,c1,c2,intersect:real;
v1,v2:Boolean;
begin
//return true if lines cross
// if line if verticle do not workout gradient
if ((line1.point[1].x)-(line1.point[0].x))=0 then
v1:=true // remember line 1 is verticle
else
begin
m1 := ((line1.point[1].y)-(line1.point[0].y))/((line1.point[1].x)-(line1.point[0].x));
c1 := line1.point[0].y - (line1.point[0].x*m1);
end;
if ((line2.point[1].x)-(line2.point[0].x))=0 then
v2:=true // remember line 2 is verticle
else
begin
m2 := ((line2.point[1].y)-(line2.point[0].y))/((line2.point[1].x)-(line2.point[0].x));
c2 := line2.point[0].y - (line2.point[0].x*m2);
end;
if ((NOT(m1=m2)) and (NOT(v1 or v2))) then // non parrellel and non verticle
begin
//lines cross find where
intersect := (c2-c1)/(m1-m2); //line intersect solved for x
if ((round(intersect)>= Min(line1.point[0].x,line1.point[1].x))
and(round(intersect)<=max(line1.point[0].x,line1.point[1].x))
and(round(intersect)>=min(line2.point[0].x,line2.point[1].x))
and(round(intersect)<=max(line2.point[0].x,line2.point[1].x))) then
result := true
else
result := false
end
else if (v1 and v2) then // both lines are parralel
begin
// double verticle parallel exeption
if (((line1.Point[0].y>=min(line2.Point[0].y,line2.Point[1].y))
and(line1.Point[0].y<=max(line2.Point[0].y,line2.Point[1].y)))
or ((line1.Point[1].y>=min(line2.Point[0].y,line2.Point[1].y))
and(line1.Point[1].y<=max(line2.Point[0].y,line2.Point[1].y)))
or ((line2.Point[0].y>=min(line1.Point[0].y,line1.Point[1].y))
and(line2.Point[0].y<=max(line1.Point[0].y,line1.Point[1].y)))
or ((line2.Point[1].y>=min(line1.Point[0].y,line1.Point[1].y))
and(line2.Point[1].y<=max(line1.Point[0].y,line1.Point[1].y)))) then
result := true
else
result := false;
end
else if (v1 and not v2) then // line 1 is verticle and line 2 is not
begin
if ((((line1.Point[0].x*m2+c2)>=min(line1.Point[0].y,line1.Point[1].y))
and ((line1.Point[0].x*m2+c2)<=max(line1.Point[0].y,line1.Point[1].y)))) then
result := true
else
result := false
end
else if (v2 and not v1) then // line 2 is verticle and line 1 is not
begin
if (((line2.Point[0].x*m1+c1)>min(line2.Point[0].y,line2.Point[1].y))
and ((line2.Point[0].x*m1+c1)<max(line2.Point[0].y,line2.Point[1].y))) then
result := true
else
result := false
end
else if (m1=m2) then // parrellel non verticle lines
begin
if (((line1.Point[0].x>=min(line2.Point[0].x,line2.Point[1].x))
and(line1.Point[0].x<=max(line2.Point[0].x,line2.Point[1].x)))
or ((line1.Point[1].x>=min(line2.Point[0].x,line2.Point[1].x))
and(line1.Point[1].x<=max(line2.Point[0].x,line2.Point[1].x)))
or ((line2.Point[0].x>=min(line1.Point[0].x,line1.Point[1].x))
and(line2.Point[0].x<=max(line1.Point[0].x,line1.Point[1].x)))
or ((line2.Point[1].x>=min(line1.Point[0].x,line1.Point[1].x))
and(line2.Point[1].x<=max(line1.Point[0].x,line1.Point[1].x)))) then
result := true
else
result := false;
end;
end;
but according to my code all lines always intersect..... thus I have made a mistake... am I doing this in a silly way any ideas what I have done wrong?
There are better ways of detecting whether two sets of lines intersect, but don't worry about that for now.
I'm concerned that your program even ran long enough for you to detect that everything intersects; you iterate beyond the bounds of your arrays, so your program should have crashed. Always leave range checking enabled.
If you're going to be doing geometry, you should make sure to differentiate between lines and line segments. In two dimensions, non-parallel lines always intersect. Even parallel lines can intersect if they are coincident. It would also behoove you to get the spellings of parallel and vertical correct.
Your calculation of intersect is wrong. You need to divide the difference in slopes by the difference in y-intercepts:
if c1 = c2 then
intersect := c1
else
intersect := (m1 - m2) / (c2 - c1);
If both lines are vertical, then it's not enough to check whether they overlap in their y coordinates. You also need to check that their x coordinates are equal. Likewise, with parallel non-vertical lines, you need to check whether the y-intercepts are equal.
If you fix all those problems and still get wrong results, then it's time to dust off the debugger. Find a pair of line segments that your function returns true for and yet do not really intersect. Call the function on those values and step through your function with the debugger. To make debugging easier, you'll want to split those many-line-long conditional expressions into several intermediate variables so you can check each one separately. Determine which calculation is wrong, and then fix it. Make sure your test dataset contains elements that will exercise each possible conditional path in your function.
Related
It keeps saying that i,cont and divisor are not being used and I cant figure out why, everthing is assinged and used at some point.
I can compile it using the command prompt but it doesnt display what it should as a result it shows simbols, and if I try another compiler it just shows the local variable not used message it is not an error it just says: note Local variable "i" not used
HereĀ“s the code
program tarea1;
var
n,m,i,divisor,cont: integer;
begin
readln(n);
readln(m);
if (n<1) or (m<n) then
end.
i:=n;
for i to m do
begin
divisor:=2;
while (sqrt(i) >= divisor) and (i mod divisor <> 0) do
divisor:=divisor + 1;
if (divisor > sqrt(i)) then
writeln(i': es primo')
else
begin
for i to (i div 2) do
begin
divisor:= 2;
cont:= 0;
write(i':');
repeat
if i mod divisor = 0 then
begin
write(' divisor ');
divisor:=succ(divisor);
cont:=succ(cont);
until cont = 6;
writeln();
end;
end;
end;
end;
end.
Your program is not as long as you seem to think.
Around line 14 the code is:
if (n<1) or (m<n) then
end.
This "end." is the end of the source code - everything after that is not part of your program!
If you want to terminate your program if n is less than 1 or m is less then m then you should write
if (n<1) or (m<n) then
exit;
I'm trying to make a Test Bench where a file of one single line, where posible characters are "1" and "0". I've to read them all, and use one by one as input in my DUT.
So, in my TB, I've defined a process like the following, in order to read the file an pass it values to my DUT.
stim_proc: process
file input_file: TEXT is in "DatosEntrada.dat";
variable rdline : LINE;
variable line_content : string ( 1 to 4);
variable readed_char : character;
variable j : integer := 0;
begin
while not endfile(input_file) loop
readline(input_file, rdline);
--read(rdline, line_content);
for j in 1 to rdline'length-1 loop
readed_char := line_content(j);
if (readed_char = '1') then
input <= '1';
else
input <= '0';
end if;
wait for clk_period;
end loop;
end loop;
end process;
I'm reading the first (and only) line of my file with the first readline execution. After this, this loop shouldn't execute again.
Then, data from file should be inside rdline. So I've to process it. In order to do it, I've tried to loop over rdline length, but this loop doesn't execute.
for j in 1 to rdline'length-1 loop
So I thought I need to read this line in order to loop over it, and tried to move its data to a string var. The problem is that vector var like string need to have a defined size, and I don't know the file line size.
I've tried reading each time 4 chars from rdline into a string, process it, then repeat. However, I couldn't make it work.
I've found quite lot examples about reading files which have defined line formats, like columns or expected integers.
But how can I read an unknown text of one line?
This readed_char := line_content(j); doesn't work when line_content isn't loaded. Otherwise your attempt to read values is basically sound.
The end of line is not contained in a read LINE buffer, there's no reason to not read the last character of rdline. An end of line is signaled by one or more format effectors other than horizontal tab, and just the line contents are present.
There's also this inference that you have some relationship to a clock edge and not just a clock period. The following example shows that. Note you can also supply an offset from an edge using a wait for time_value.
A loop constant is declared in the loop statement. The variable j you declared is not the same j the loop uses. The loop statement hides the j in the outer declarative region (the variable declaration in the process statement).
Your code treats any other character in the string buffer than '1' as a '0'. I didn't change that, do demonstrate it. You should be aware of the impact.
A LINE is an allocated string of some length dependent on the length of a line in your read file. Every time you call readline the string rdline points to is updated. It doesn't leak memory, the previous buffer rdline pointed to released. You can read the length by using the 'RIGHT attribute or as in this case simply consume all the characters.
There may be line length limits in a VHDL tool implementation. There are none defined in the standard other than the maximum length of a string (POSITIVE'RIGHT).
An MCVE:
library ieee;
use ieee.std_logic_1164.all;
use std.textio.all;
entity foo is
end entity;
architecture fum of foo is
signal input: std_logic ;
signal clk: std_logic := '0';
constant clk_period: time := 10 ns;
begin
stim_proc:
process
file input_file: TEXT is in "DatosEntrada.dat";
variable rdline: LINE;
-- variable line_content : string ( 1 to 4);
-- variable readed_char : character;
-- variable j: integer := 0;
begin
while not endfile(input_file) loop
readline(input_file, rdline);
--read(rdline, line_content);
-- for j in 1 to rdline'length - 1 loop -- EOL not in rdline
for j in rdline'range loop
-- readed_char := line_content(j);
-- if readed_char = '1' then
if rdline(j) = '1' then -- changed
input <= '1';
else
input <= '0';
end if;
-- wait for clk_period; -- sync to edge instead
wait until falling_edge(clk); -- input related to clk edge
end loop;
end loop;
wait; -- added prevents needless loops
end process;
CLOCK:
process
begin
wait for clk_period/2;
clk <= not clk;
if now > 32 * clk_period then
wait;
end if;
end process;
end architecture;
And for DatosEntrada.dat containing:
11011110001HELLO11230000
That produces:
Where you can see all non '1' characters are interpreted as '0'.
I have to calculate first 3000 items of a sequence given as follows:
a_1=1,
a_n+1 = smallest integer > a_n, for which for every (not necessarily different) 1<= i,j,k <= n+1 applies (a_i+a_j not equal 3*a_k)
I have written code (in Magma) that works correctly, but its time complexity is obviously way too large. I am asking if there is a way to reduce the time complexity. I had an idea to somehow move the inner for loop (which is the one causing havoc) out in a way of making an array of all the sums, but I cant get it to work right. Attaching my code below:
S:=[1];
for n:=2 to 3000 do
new:=S[n-1];
repeat
flag:=0;
new+:=1;
for i,j in S do
if (i+j eq 3*new) or (i+new eq 3*j) then
flag:=1;
break i;
end if;
end for;
until flag eq 0;
S[n]:=new;
end for;
print S[2015];
P.S.: If it helps, I also know Python, Pascal and C if you prefer any of those languages.
I copied your program in MAGMA. Run time for n=2978 was 4712.766 seconds. I changed your program as follow and result was amazing. Run time for changed version for n=3000 was 41.250 seconds.
S:=[1];
for n:=2 to 3000 do
new:=S[n-1];
repeat
flag:=0;
new+:=1;
for i in S do
if ((3*new-i) in S) or ((i+new)/3 in S) then
flag:=1;
break i;
end if;
end for;
until flag eq 0;
S[n]:=new;
end for;
I'm trying to create a basic program in Maple that runs the Collatz sequence when given a number (n) from the user. For those that don't know, the Collatz sequence is basically "If the number given is odd, do 3n + 1, if it is even, divide by 2 and continue to do so for every answer. Eventually, the answer will reach 1"
I'm trying to grab the number of iterations that the sequence is performed, say if the sequence is run through 10 times, it prints that out. Here is my current code:
Collatz := proc (n::posint)
if type(n, even) then (1/2)*n
else 3*n+1
end if
end proc
CollSeq := proc (n::posint)
local i;
i := n;
while 1 < i do
lprint(i);
i := Collatz(i)
end do
end proc
This so far works, and if the proc CollSeq(50) is entered, it will perform the Collatz sequence on 50 until it reaches 1. The bit I am stuck on is the length of the sequence. I have read around and learned that I might be able to use the nops([]) function of Maple to get the length of the sequence. Here is what I have tried:
CollLen := proc (n::posint)
local c;
c := CollSeq(n);
print(nops([c]))
end proc
I have a feeling this is horribly wrong. Any help would be much appreciated.
Many Thanks
Your function fails to return the actual sequence of values. You need to accumulate it as you go through the loop.
CollSeq := proc (n::posint)
local i, s;
i := n;
s := i;
while 1 < i do
lprint(i);
i := Collatz(i);
s := s, i;
end do;
s;
end proc
The lprint() command just prints its argument to the terminal (showing it on screen), it DOES not save it in a list. And nops() or a better command numelems() counts the number of elements in a list! So putting nops around something that has lprint will not count the number of things. Instead of using lprint in your second function (procedure), define a list, or better than list, an array and in the lprint-line, use a command to append the new number to your growing collection. If you want to see these numbers, just print this collection. Now this time, your third function can have a meaning and it will work as you expected.
Here is the closest fix to your codes.
Collatz := proc( n :: posint )
if type(n, even) then
return( n/2 ):
else
return( 3*n+1 ):
end if:
end proc:
CollSeq := proc ( n :: posint )
local
i :: posint,
c :: 'Array'( posint ):
i := n:
c := Array([]):
while 1 < i do
ArrayTools:-Append( c, i ):
i := Collatz( i ):
end do:
return( c ):
end proc:
CollLen := proc ( n :: posint )
local c :: posint:
c := CollSeq( n ):
return( numelems( c ) ):
end proc:
Here is a screenshot of using them in a Maple worksheet.
Why do I use an array and not a list? Because if you use a list which is immutable, each time you want to add an element to it, in fact it is defining a new list. It is not a memory efficient way, while array is mutable and your edits modifies the array itself. See the help pages on these in Maple.
And looking at your codes, it seems you have the same problem that some of my students in their first programming course usually have, return and print are not the same thing. If you really want a "print" action, that is fine, but you should not expect that the printed value be the output of the function unless you are using a return line inside the function that returns the same value of the print as well. For example you can have print(c): before return(c): in the second function above. So it both prints the sequence on the terminal and returns it to be used by another function or line of code.
I keep getting that error. Here's the code (it's for GCD):
Euc := proc (a, b)
if b = 0 then a;
else c := b;
d := a mod b;
b := d; a := c;
end if;
end proc;
I never use Maple because it gives me a headache and the documentation is a nightmare, but this assignment has to be done all in Maple... if I'm having trouble with simple GCD, I don't see me writing RSA and El Gamal by Wednesday :s
edit: Fixed it with
Euc := proc (a, b)
if b = 0 then a;
else c := b;
d := a mod b;
Euc(c,d);
end if;
end proc;
But any I'd still like to know what the problem was, in case I have to do something similar again.
Your first version attempted to assign to the formal parameters of the procedure. That was the problem.
Suppose you call your original Euc and pass in 12 for parameter a and 8 for parameter b. Inside the body of Euc, as it runs in this instance, a evaluates to 12 and a does not evaluate to a name to which you can make an assignment. When you try and make an assignment to a or b inside Euc then you see that error.