This is part one of my attempt to find an answer to my question wireframes in Mathematica.
Given a set of line segments how does one join two segments that are connected AND lie on the same line. For instance consider the line segments l1 = {(0,0), (1,1)}
and l2 = {(1,1), (2,2)}
. These two line segments can be combined into one line segment, namely l3 = {(0,0), (2,2)}
. This is because l1
and l2
share the point (1,1)
and the slope of each line segment is the same. Here is a visual:
l1 = JoinedCurve[{{{0, 2, 0}}}, {{{0, 0}, {1, 1}}}, CurveClosed -> {0}];
l2 = JoinedCurve[{{{0, 2, 0}}}, {{{1, 1}, {2, 2}}}, CurveClosed -> {0}];
Graphics[{Red, l1, Blue, l2}, Frame -> True]
One thing to notice is that in the above example l1
and l2
can be combined into one line specified by 3 points, i.e. {{0,0},{1,1},{2,2}}
.
The first part of this question is: Given a set of line segments specified by 2 points, how do you reduce this set to have a set with the minimum amount of duplicate points. Consider this made up example:
lines = {
{{0,0}, {1,1}},
{{3,3}, {2,2}},
{{2,2}, {1,1}},
{{1,1}, {0.5,0.5}},
{{0,1}, {0,2}},
{{2,3}, {0,1}}
}
What I want is a function say REDUCE
that gives me the following output:
R = {
{{0,0}, {1,1}, {2,2}, {3,3}},
{{1,1}, {0.5,0.5}},
{{2,1}, {0,1}, {0,2}}
}
The only duplicate we need is {1,1}
. The way I did this was as follows: I put the first line in R
Then I looked at the next line in lines
and noticed that no end point matches an endpoint in the lines of R
so I added this new line to R
. The next line in lines
is {{2,2},{1,1}}
, the endpoint {1,1}
matches the first line in R
so I appended {2,2}
to line in R
. Now I add {{1,1}, {0.5,0.5}}
to R
and I also add {{0,1}, {0,2}}
. Since the last line in lines
has an endpoint that matches one in R
I appended it and so we have {{2,1}, {0,1}, {0,2}}
. Finally I look at all the lines in R
and see if any of the endpoints match, in this case the line {{3,3}, {2,2}}
matches the right endpoint of the first line in R
so I append {3,3}
thus eliminating the need for {2,2}
.
This may not be the best way to do it, in the sense that it may not give you the best reduction. In any case, assuming that we have this reduction function then we can check if we need all the points to describe a line. This can be done as follows:
If we have more than 3 points describing the line, check if the first 3 points are collinear, if they are, remove the middle one and do the check on the set of the 2 endpoints and a new point. If they are not collinear then shift by one point and check the next 3 points.
The reason I'm asking this question is because I want to reduce the amount of points needed to describe a 2D figure. Try the following:
g1 = ListPlot3D[
{{0, -1, 0}, {0, 1, 0}, {-1, 0, 1}, {1, 0, 1}, {-1, 1, 1}},
Mesh -> {2, 2},
Boxed -> False,
Axes -> False,
ViewPoint -> {2, -2, 1},
ViewVertical -> {0, 0, 1}
]
The following Mathematica 8 function changes a 3D object into a list of lines (a line is a list of 2 points) that describe the wire frame of the object:
G3TOG2INFO[g_] := Module[{obj, opt},
obj = ImportString[ExportString[g, "PDF", Background -> None], "PDF"][[1]];
opt = Options[obj];
obj = Cases[obj, _JoinedCurve, \[Infinity]];
obj = Map[#[[2]][[1]] &, obj];
{obj, opt}
]
Note that in Mathematica 7 we have to substitude _JoinedCurve
by _Line
. Applying the function on g1
we obtain
{lines, opt} = G3TOG2INFO[g1];
Row[{Graphics[Map[Line[#] &, lines], opt], Length@lines}]
There are 90 line segments in there but we only need 12 (If I didn't make any mistake on the counting of straight lines).
So there you have the challenge. How do we manipulate lines
to have minimum amount of information needed to describe the figure.