What does it mean when a dxf vertex with a bulge factor is not followed by an end point? - autocad

I have a DXF file that was exported from a drawing of a simple arc that starts at (0, 0), ends at (2, 0) and has a radius of 1.0. I would expect the LWPOLYLINE to be made up of two vertices with the first containing the start point and bulge factor, and the second point simply containing the end point. However, the end point contains a bulge factor as well. How is this bulge point to be interpreted? Shouldn't all vertices with a bulge be followed by another point that defines the end point?
AcDbPolyline
90
2
70
0
43
0.0
10
0.0 -----------------> x1
20
0.0 -----------------> x2
42
0.9999999999999998 ---> p1 to p2 w/ bulge = 1, makes sense
10
2.0 -----------------> x2
20
0.0 -----------------> y2
42
1.330537671996453 ----> why does p2 have a bulge? Shouldn't all vertices w/
a bulge be followed by another point (to define the
end point)?
0
ENDSEC

The best way to find out such details, is to test. If you don't have an AutoCAD application try Autodesk TrueView, it's free.
What I found out by testing is: the last bulge value does nothing, you can change it to any value you want or just delete it, the LWPOLYLINE looks always the same.
EDIT:
This is only true if the LWPOLYLINE isn't closed.
If the LWPOLINE is closed, group code 70=1, the last bulge and also the last start width and end width value, apply to the closing segment from the last vertex to first vertex, your example as closed polyline looks like this:

DXF group 70 is bit-coded, with bit 1 indicating that the LWPolyline entity is closed (note that this is not the same as the LWPolyline having coincident endpoints).
With bit 1 set, the bulge factor (DXF group 42) and starting & ending width values (DXF groups 40 & 41) define how the closing segment (i.e. the segment spanning the last vertex & first vertex) should appear.
You can witness the effect of this value in the following examples:
The following entmake expression with the final DXF group 42 entry omitted (and therefore interpreted as 0) produces a polyline as shown in the image:
(entmake
'(
(000 . "LWPOLYLINE")
(100 . "AcDbEntity")
(100 . "AcDbPolyline")
(090 . 3)
(070 . 1)
(010 0.0 0.0)
(010 1.0 1.0)
(010 1.0 0.0)
)
)
Whereas the following entmake expression with the final DXF group 42 entry set to -1 (=tan(-pi/4)) produces a polyline as shown in the image:
(entmake
'(
(000 . "LWPOLYLINE")
(100 . "AcDbEntity")
(100 . "AcDbPolyline")
(090 . 3)
(070 . 1)
(010 0.0 0.0)
(010 1.0 1.0)
(010 1.0 0.0)
(042 . -1.0)
)
)

Related

Matlab - Algorithm for calculating 1d consecutive line segment edges from midpoints?

So I have a rectilinear grid that can be described with 2 vectors. 1 for the x-coordinates of the cell centres and one for the y-coordinates. These are just points with spacing like x spacing is 50 scaled to 10 scaled to 20 (55..45..30..10,10,10..10,12..20,20,20) and y spacing is 60 scaled to 40 scaled to 60 (60,60,60,55..42,40,40,40..40,42..60,60) and the grid is made like this
e.g. x = 1 2 3, gridx = 1 2 3, y = 10 11 12, gridy = 10 10 10
1 2 3 11 11 11
1 2 3 12 12 12
so then cell centre 1 is 1,10 cc2 is 2,10 etc.
Now Im trying to formulate an algorithm to calculate the positions of the cell edges in the x and y direction. So like my first idea was to first get the first edge using x(1)-[x(2)-x(1)]/2, in the real case x(2)-x(1) is equal to 60 and x(1) = 16348.95 so celledge1 = x(1)-30 = 16318.95. Then after calculating the first one I go through a loop and calculate the rest like this:
for aa = 2:length(x)+1
celledge1(aa) = x(aa-1) + [x(aa-1)-celledge(aa-1)]
end
And I did the same for y. This however does not work and my y vector in the area where the edge spacing should be should be 40 is 35,45,35,45... approx.
Anyone have any idea why this doesnt work and can point me in the right direction. Cheers
Edit: Tried to find a solution using geometric alebra:
We are trying to find the points A,B,C,....H. From basic geometry we know:
c1 (centre 1) = [A+B]/2 and c2 = [B+C]/2 etc. etc.
So we have 7 equations and 8 variables. We also know the the first few distances between centres are equal (60,60,60,60) therefore the first segment is 60 too.
B - A = 60
So now we have 8 equations and 8 variables so I made this algorithm in Matlab:
edgex = zeros(length(DATA2.x)+1,1);
edgey = zeros(length(DATA2.y)+1,1);
edgex(1) = (DATA2.x(1)*2-diffx(1))/2;
edgey(1) = (DATA2.y(1)*2-diffy(1))/2;
for aa = 2:length(DATA2.x)+1
edgex(aa) = DATA2.x(aa-1)*2-edgex(aa-1);
end
for aa = 2:length(DATA2.y)+1
edgey(aa) = DATA2.y(aa-1)*2-edgey(aa-1);
end
And I still got the same answer as before with the y spacing going 35,45,35,45 where it should be 40,40,40... Could it be an accuracy error??
Edit: here are the numbers if ur interested and I did the same computation as above only in excel: http://www.filedropper.com/workoutedges
It seems you're just trying to interpolate your data. You can do this with the built-in interp1
x = [30 24 19 16 8 7 16 22 29 31];
xi = interp1(2:2:numel(x)*2, x, 1:(numel(x)*2+1), 'linear', 'extrap');
This just sets up the original data as the even-indexed elements and interpolates the odd indices, including extrapolation for the two end points.
Results:
xi =
Columns 1 through 11:
33.0000 30.0000 27.0000 24.0000 21.5000 19.0000 17.5000 16.0000 12.0000 8.0000 7.5000
Columns 12 through 21:
7.0000 11.5000 16.0000 19.0000 22.0000 25.5000 29.0000 30.0000 31.0000 32.0000

Bilinear image interpolation / scaling - A calculation example

I would like to ask you about some bilinear interpolation / scaling details. Let's assume that we have this matrix:
|100 | 50 |
|70 | 20 |
This is a 2 x 2 grayscale image. Now, I would like scale it by factor of two and my matrix looks like this:
| 100 | f1 | 50 | f2 |
| f3 | f4 | f5 | f6 |
| 70 | f7 | 20 | f8 |
so if we would like to calculate f4, the calculation is defined as
f1 = 100 + 0.5(50 - 100) = 75
f7 = 70 + 0.5(20 - 70) = 45
and now finally:
f4 = 75 + 0.5(45 - 75) = 60
However, I can't really understand what calculations are proper for f3 or f1
Do we do the bilinear scaling in each direction separately? Therefore, this would mean that:
f3 = 100 + 0.5(70 - 100) = 85
f1 = 100 + 0.5(50 - 100) = 75
Also, how should I treat f2, f6, f8. Are those points simply being copied like in the nearest neighbor algorithm?
I would like to point you to this very insightful graphic from Wikipedia that illustrates how to do bilinear interpolation for one point:
Source: Wikipedia
As you can see, the four red points are what is known. These points you know before hand and P is the point we wish to interpolate. As such, we have to do two steps (as you have indicated in your post). To handle the x coordinate (horizontal), we must calculate what the interpolated value is row wise for the top row of red points and the bottom row of red points. This results in the two blue points R1 and R2. To handle the y coordinate (vertical), we use the two blue points and interpolate vertically to get the final P point.
When you resize an image, even though we don't visually see what I'm about to say, but imagine that this image is a 3D signal f. Each point in the matrix is in fact a 3D coordinate where the column location is the x value, the row location is the y value and the z value is the quantity / grayscale value of the matrix itself. Therefore, doing z = f(x,y) is the value of the matrix at location (x,y) in the matrix. In our case, because you're dealing with images, each value of (x,y) are integers that go from 1 up to as many rows/columns as we have depending on what dimension you're looking at.
Therefore, given the coordinate you want to interpolate at (x,y), and given the red coordinates in the image above, which we call them x1,y1,x2,y2 as per the diagram - specifically going with the convention of the diagram and referencing how images are accessed: x1 = 1, x2 = 2, y1 = 2, y2 = 1, the blue coordinates R1 and R2 are computed via 1D interpolation column wise using the same row both points coincide on:
R1 = f(x1,y1) + (x - x1)/(x2 - x1)*(f(x2,y1) - f(x1,y1))
R2 = f(x1,y2) + (x - x1)/(x2 - x1)*(f(x2,y2) - f(x1,y2))
It's important to note that (x - x1) / (x2 - x1) is a weight / proportion of how much of a mix the output consists of between the two values seen at f(x1,y1) and f(x2,y1) for R1 or f(x1,y2) and f(x2,y2) for R2. Specifically, x1 is the starting point and (x2 - x1) is the difference in x values. You can verify that substituting x1 as x gives us 0 while x2 as x gives us 1. This weight fluctuates between [0,1] which is required for the calculations to work.
It should be noted that the origin of the image is at the top-left corner, and so (1,1) is at the top-left corner. Once you find R1 and R2, we can find P by interpolating row wise:
P = R2 + (y - y2)/(y2 - y1)*(R1 - R2)
Again, (y - y2) / (y2 - y1) denote the proportion / mix of how much R1 and R2 contribute to the final output P. As such, you calculated f5 correctly because you used four known points: The top left is 100, top right is 50, bottom left is 70 and bottom right is 20. Specifically, if you want to compute f5, this means that (x,y) = (1.5,1.5) because we're halfway in between the 100 and 50 due to the fact that you're scaling the image by two. If you plug in these values into the above computation, you will get the value of 60 as you expected. The weights for both calculations will also result in 0.5, which is what you got in your calculations and that's what we expect.
If you compute f1, this corresponds to (x,y) = (1.5,1) and if you substitute this into the above equation, you will see that (y - y2)/(y2 - y1) gives you 0 or the weight is 0, and so what is computed is just R2, corresponding to the linear interpolation along the top row only. Similarly, if we computed f7, this means we want to interpolate at (x,y) = (1.5,2). In this case, you will see that (y - y2) / (y2 - y1) is 1 or the weight is 1 and so P = R2 + (R1 - R2), which simplifies to R1 and is the linear interpolation along the bottom row only.
Now there's the case of f3 and f5. Those both correspond to (x,y) = (1,1.5) and (x,y) = (2,1.5) respectively. Substituting these values in for R1 and R2 and P for both cases give:
f3
R1 = f(1,2) + (1 - 1)/(2 - 1)*(f(2,2) - f(1,2)) = f(1,2)
R2 = f(1,1) + (1 - 1)/(2 - 1)*(f(1,2) - f(1,1)) = f(1,1)
P = R1 + (1.5 - 1)*(R1 - R2) = f(1,2) + 0.5*(f(1,2) - f(1,1))
P = 70 + 0.5*(100 - 70) = 85
f5
R1 = f(1,2) + (2 - 1)/(2 - 1)*(f(2,2) - f(1,2)) = f(2,2)
R2 = f(1,1) + (2 - 1)/(2 - 1)*(f(1,2) - f(1,1)) = f(1,2)
P = R1 + (1.5 - 1)*(R1 - R2) = f(2,2) + 0.5*(f(2,2) - f(1,2))
P = 20 + 0.5*(50 - 20) = 35
So what does this tell us? This means that you are interpolating along the y-direction only. This is apparent when we take a look at P. Examining the calculations more thoroughly of P for each of f3 and f5, you see that we are considering values along the vertical direction only.
As such, if you want a definitive answer, f1 and f7 are found by interpolating along the x / column direction only along the same row. f3 and f5 are found by interpolating y / row direction along the same column. f4 uses a mixture of f1 and f7 to compute the final value as you have already seen.
To answer your final question, f2, f6 and f8 are filled in based on personal preference. These values are considered to be out of bounds, with the x and y values both being 2.5 and that's outside of our [1,2] grid for (x,y). In MATLAB, the default implementation of this is to fill any values outside of the defined boundaries to be not-a-number (NaN), but sometimes, people extrapolate using linear interpolation, copy the border values, or perform some elaborate padding like symmetric or circular padding. It depends on what situation you're in, but there is no correct and definitive answer on how to fill in f2, f6 and f8 - it all depends on your application and what makes the most sense to you.
As a bonus, we can verify that my calculations are correct in MATLAB. We first define a grid of (x,y) points in the [1,2] range, then resize the image so that it's twice as large where we specify a resolution of 0.5 per point rather than 1. I'm going to call your defined matrix A:
A = [100 50; 70 20]; %// Define original matrix
[X,Y] = meshgrid(1:2,1:2); %// Define original grid of points
[X2,Y2] = meshgrid(1:0.5:2.5,1:0.5:2.5) %// Define expanded grid of points
B = interp2(X,Y,A,X2,Y2,'linear'); %// Perform bilinear interpolation
The original (x,y) grid of points looks like:
>> X
X =
1 2
1 2
>> Y
Y =
1 1
2 2
The expanded grid to expand the size of the matrix by twice as much looks like:
>> X2
X2 =
1.0000 1.5000 2.0000 2.5000
1.0000 1.5000 2.0000 2.5000
1.0000 1.5000 2.0000 2.5000
1.0000 1.5000 2.0000 2.5000
>> Y2
Y2 =
1.0000 1.0000 1.0000 1.0000
1.5000 1.5000 1.5000 1.5000
2.0000 2.0000 2.0000 2.0000
2.5000 2.5000 2.5000 2.5000
B is the output using X and Y as the original grid of points and X2 and Y2 are the points we want to interpolate at.
We get:
>> B
B =
100 75 50 NaN
85 60 35 NaN
70 45 20 NaN
NaN NaN NaN NaN

Knight's Tour on a 5 x 5 Board Start from any Square?

I'd just like to check my logic here...
I wrote code to solve the Knight's Tour and it works well for 8x8 boards starting the Knight at any square.
But... on a 5x5 board I show no solution possible when starting at square (0, 1).
What I tried for 5x5 starting the Knight at Row 0, Col 1:
Warnsdorff's path
Added Roth (tie breakers based on Euclidean distance from center).
Since those did not produce a solution I did code that is just basic recursion with backtracking to test every possible path -- also no solution found when starting a 5x5 on 1, 0.
I looked everywhere for a list of exhaustive solutions to the 5x5 board but found none.
Is it that there just is no solution for 5x5 when starting at square 0, 1?
Thank you!
1 2 3 4 5
1 304 0 56 0 304
2 0 56 0 56 0
3 56 0 64 0 56
4 0 56 0 56 0
5 304 0 56 0 304
This might help.If knights starts at (1,1) there will be 304 possible knights tour and if it starts at (1,2) then there will be NO knights tour.Similarly if knight starts at (3,3) then there are 64 possible knights tour.
Correct, there is no solution when you start at any of the squares adjacent to a corner square.
By a simple coloring argument, you must start on a square the same color as a corner.
Here's an English explanation for why no knight's tour on a 5x5 board can start at (0,1). This is the "coloring" argument that Ben Voigt alludes to.
Imagine that you are coloring the board like a checkerboard, where the center square is white. Then the board has 13 white squares and 12 black squares.
Every time a knight moves, it changes the color of its square. So if you start on a black square, after 1 move you'll be on a white square (with your original square used up), after 2 moves you'll be on a black square (with 1 black and 1 white square used up), after 3 moves you'll be on a white square (with 2 blacks and 1 white square used up).
Continuing this pattern, after 24 moves, you will have used up 12 black squares, 12 white squares, and your knight will be on a white square.
But the only unused square is white!
It's impossible for you to get to that last square, because it's the same color as the square your knight is on.
import numpy as np
import matplotlib.pyplot as plt
import copy
import time
grid_points = []
grid_nom = set()
for i in range(5):
for j in range(5):
grid_nom.add(f"A_{i+1}_{j+1}")
grid_points.append([f"A_{i+1}_{j+1}", i+1, j+1])
origin = "A_2_2"
chain_propogation = [[origin]]
chain_length = 1
grid_num = len(grid_nom)
start = time.time()
while chain_length <= grid_num-1:
new_chain_propogation = []
for chain in chain_propogation:
mother_grid = copy.copy(grid_nom)
last_node = chain[-1]
rest_grid = mother_grid.difference(set(chain))
children = []
for point in rest_grid:
if (int(last_node.split('_')[1])-int(point.split('_')[1]))*(int(last_node.split('_')[2])-int(point.split('_')[2])) in [2, -2]:
children.append(point)
if len(children) == 0:
pass
else:
for child in children:
new_chain = copy.copy(chain)
new_chain.append(child)
new_chain_propogation.append(new_chain)
chain_propogation = new_chain_propogation
chain_length += 1
end = time.time()
print(f'Starting from {origin} in this 5x5 takes {end-start} seconds.')
print(f'There are {len(new_chain_propogation)} solutions as follows: {new_chain_propogation}.')
## example of one tour route
n = 1
tour_0 = []
for point in new_chain_propogation[n-1]:
tour_0.append([int(point.split('_')[1]), int(point.split('_')[2])])
fig, ax = plt.subplots(figsize=(4, 3))
grid_arry = np.array(grid_points)
ax.plot(np.array(tour_0).T[0], np.array(tour_0).T[1], color="r")
ax.scatter(np.array(tour_0).T[0], np.array(tour_0).T[1], marker = "o", color="b")

Using Bi-cubic Interpolation

I read about it on Wikipedia, theory sounds good, but I don't know to apply to practice.
I have an small example like this one:
Original Image Matrix
1 2
3 4
If I want to double size the image, then the new matrix is
x x x x
x x x x
x x x x
x x x x
Now, the fun part is how to transfer old values in original matrix to the new matrix, I intend to do like this
1 x 2 x
x x x x
3 x 4 x
x x x x
Then applying the Bi cubic Interpolation on it (at this moment just forget about using 16 neighbor pixel, I don't have enough space to demonstrate such a large matrix here).
Now my questions are:
1. Do I do the data transferring (from old to new matrix) right? If not, what should it look like?
2. What should be the value of x variables in the new matrix? to me , this seems correct because at least we have some values to do the calculation instead of x notations.
1 1 2 2
1 1 2 2
3 3 4 4
3 3 4 4
3. Will all of the pixels in new matrix be interpolated? Because the pixels at the boundary do not have enough neighbor pixels to perform the calculation.
Thank you very much.
Interpolation means estimating a value for points that don't physically exist. You need to start with a coordinate system, so let's just use two incrementing integers for X position and Y position.
0, 0 1, 0
0, 1 1, 1
Your output requires 4x4 pixels which should be spaced at 0.5 intervals instead of the 1.0 intervals of the input:
-0.25,-0.25 0.25,-0.25 0.75,-0.25 1.25,-0.25
-0.25, 0.25 0.25, 0.25 0.75, 0.25 1.25, 0.25
-0.25, 0.75 0.25, 0.75 0.75, 0.75 1.25, 0.75
-0.25, 1.25 0.25, 1.25 0.75, 1.25 1.25, 1.25
None of the coordinates in the output exist in the input, so they'll all need to be interpolated.
The offset of the first coordinate of -0.25 is chosen so that the first and last coordinate will be equal distances from the edges of the input, and is calculated by the difference between the output and input intervals divided by 2. For example if you wanted a 10x zoom the interval is 0.1, the initial offset is (0.1-1)/2 and the points would be (-0.45, -0.35, -0.25, -0.15, ... 1.35, 1.45).
The Bicubic algorithm will require data for points outside of the original image. The easiest solution comes when you use a premultiplied alpha representation for your pixels, then you can just use (0,0,0,0) as the value for anything outside the image boundaries.

How to determine game end, in tic-tac-toe?

I'm developing tic-tac-toe game, and I need algorithm to check when game ends(and who win).
In 3x3 game I would check each possible win-situation(there is 8 capabilities). But in 7x7(needed 4 signs in a row or collumn, or diagonal)is a lot of possible win patterns.
If you are using a bitboard for each player, you can use bit shift operations to test a board for a win.
The bitboard would have following structure:
6 14 22 30 38 46 54
5 13 21 29 37 45 53
4 12 20 28 36 44 52
3 11 19 27 35 43 51
2 10 18 26 34 42 50
1 9 17 25 33 41 49
0 8 16 24 32 40 48
If the player occupies a position in the game board, then the associated bit would be 1 otherwise 0 (notice that bits 7, 15, 23, ... are 0). To check if the player has a winning board you could use following function:
bool haswon(int64_t board)
{
int64_t y = board & (board >> 7);
if (y & (y >> 2 * 7)) // check \ diagonal
return true;
y = board & (board >> 8);
if (y & (y >> 2 * 8)) // check horizontal -
return true;
y = board & (board >> 9);
if (y & (y >> 2 * 9)) // check / diagonal
return true;
y = board & (board >> 1);
if (y & (y >> 2)) // check vertical |
return true;
return false;
}
With the help of a example I will try to explain: The following bitboard of one player includes beside vertical and diagonal wins a winning combination in the first row.
0101010
1110111
0111011
1101110
0001000
1010101
0011110 ... four occupied positions --> winning board
The steps for the horizontal check are:
y = board & (board >> 8)0101010 0010101 0000000
1110111 0111011 0110011
0111011 0011101 0011001
1101110 & 0110111 = 0100110
0001000 0000100 0000000
1010101 0101010 0000000
0011110 0001111 0001110
y & (y >> 2 * 8)0000000 0000000 0000000
0110011 0001100 0000000
0011001 0000110 0000000
0100110 & 0001001 = 0000000
0000000 0000000 0000000
0000000 0000000 0000000
0001110 0000011 0000010
The horizontal check results in a board with one bit set, this means the board includes a win and the function returns true.
I have used a similar function to check a connect four game for a win. I saw this fascinating function in the sources to The Fhourstones Benchmark from John Tromp.
While a very basic approach is to look at runs in all the directions from every single cell, here are an approach then only ever checks a cell in a single "line" once. A "line" is a row, column, or diagonal that can possibly win, like in a Vegas slot machine :)
For each "line", move to start of that "line" and;
Set counter to 0.
For each cell in the "line" (traversing the line in order):
If the cell is P1 and counter is >= 0, add one to counter
If counter = 4 then P1 wins.
If the cell is P1 and counter is negative, set counter to 0
If the cell is P2 and counter is <= 0, subtract one from counter
If counter = -4 then P2 wins
If the cell is P2 and counter is positive, set counter to 0
Important Edit: If the cell contains neither P1 or P2, reset counter to 0 (doh!). I omitted this trivial but required step. Otherwise "11-11" would be counted as a win.
The "lines" can be traversed given a starting point and row/column offset per iteration (e.g. start at (0,0) and advance (1,1) for longest diagonal from NW to SE). Diagonals with lengths less than 4 can avoid being checked entirely, of course.
Happy coding.
loop though all positions. For each position check the four fields down diagonal-down-right and right (always including the field itself). Put in apropriate checks to avoid blowing up you app when you are checking fields that don't exist.
Simple. Make 4 for loops, for all rows, columns, increasing diagonals, decreasing diagonals.
In each, test if there are 4 consecutive pieces.

Resources