UPDATE
There may be some confusion as to the specification of the problem. kguler's answer doesn't really answer the question. I'll try to elaborate a bit at the bottom of the post.
One problem with the solutions of the custom arrow shaft question is the specification of the edge thickness. In the design of the arrows, the thickness of the edge in relation to the overall size of the arrow is part of the effect one wants to achieve.
Unfortunately, the two methods that can be used to specify the edge thickness, namely Thickness
and AbsoluteThickness
are not sufficient for this goal. As an easy illustration of the problem, I'll draw some circles here.
Thickness
Graphics[{FaceForm[], EdgeForm[{Thickness[0.1], Blue}], Disk[{0, 0}]}, ImageSize -> 200]
Add some identical (!) copies of the same circle:
Graphics[{FaceForm[], EdgeForm[{Thickness[0.1], Blue}],
Disk[{0, 0}], Disk[{3, 0}], Disk[{6, 0}]}, ImageSize -> 600]
The number in the Thickness
specification is a fraction of the horizontal image extent.
AbsoluteThickness
Graphics[{FaceForm[], EdgeForm[{AbsoluteThickness[20], Blue}], Disk[{0, 0}]}, ImageSize -> 200]
Now, let's reduce the image size:
Graphics[{FaceForm[], EdgeForm[{AbsoluteThickness[20], Blue}], Disk[{0, 0}]}, ImageSize -> 70]
In both cases, the ratio between the edge thickness and the object size changes. Sometimes that may be desirable, but here it isn't.
What is needed is either a thickness specification that is related to object size or a status variable from which the current image extent can be read, so that with known object size the edge thickness can be scaled. The latter would be something akin to CurrentValue
or so.
Something self-referential like
pl = Graphics[{Text[Norm@PlotRange[pl][[2]] // Dynamic, {10, 10}]}]
could work, but is rather ugly and, frankly, scary. When I changed PlotRange
to ImageSize
I got my frontend hanging.
Any ideas?
UPDATE
Trying to further clarify the question.
First, some definitions:
We have one or more objects with a given object size. In this case, circles with a radius of one. This size is given in Mathematica's internal, dimensionless canvas coordinate system.
We want to specify the object edge thickness in terms of this object size. However, MMA doesn't have that capability. You have to either specify the EdgeForm
as Thickness
(which measures edge thickness as a fraction of the horizontal PlotRange
) or as AbsoluteThickness
(which measures edge thickness in terms of printer's points, 1/72 of an inch).
The PlotRange
is the dimensionless size of MMA's canvas as displayed in the figure. It is fully independent of the ImageSize
, which determines the graphics's physical size in printer's points.
This leads to the following problem :
Graphics[{FaceForm[], EdgeForm[{Thickness[0.1], Blue}], Disk[{0, 0}]}]
Graphics[{FaceForm[], EdgeForm[{Thickness[0.1], Blue}], Disk[{0, 0}], Disk[{3, 0}], Disk[{6, 0}]}]
Both figures have the same ImageSize
, but the PlotRange
has increased (more of MMA's internal canvas is revealed), which means that edge thickness related to object size increases too, since the thickness in canvas terms increases whereas the object remains at the same canvas size. The circle's edge now takes up most of the circle's area, which wasn't the case before.
If we specify thickness with AbsoluteThickness
we get:
Graphics[{FaceForm[], EdgeForm[{AbsoluteThickness[10], Blue}], Disk[{0, 0}]}]
Graphics[{FaceForm[], EdgeForm[{AbsoluteThickness[10], Blue}],
Disk[{0, 0}], Disk[{3, 0}], Disk[{6, 0}]}]
In this case, the edge thickness in points remains the same, but the object size in points decreases because the ImageSize
in points is the same, but more objects have to be fitted in that same space, so they get less points allotted per object. Again, the edge thickness related to the object size changes.
Introducing ThicknessC
as the desired edge thickness and ObjectSizeC
the object size, both in canvas terms, we have
ThicknessC = C*ObjectSizeC
with C the desired constant ratio of these two variables. C is responsible for the appearance of the object. For instance, we might want to have a circle where the edge thickness is 1/10 of its diameter, so C = 1/10.
Since Thickness
is a fraction of the horizontal PlotRange
we have:
ThicknessC = Thickness*PlotRange (hor.)
and hence, to get the desired edge thickness ThicknessC
, we have to specify a Thickness
given by:
Thickness = C * ObjectSizeC / PlotRange (hor.)
Similarly, we have:
Thickness = AbsoluteThickness / ImageSize
and hence,
AbsoluteThickness = C * ObjectSizeC * ImageSize / PlotRange (hor.)
It seems that any object related edge thickness specification minimally needs to know the final PlotRange
of the figure, which, on its turn, is determined by all objects in the figure.
If we're going to use AbsoluteThickness
(but why would we bother?) we'd also need awareness of the ImageSize
in addition to knowledge about PlotRange
.
So, there you have it. The problem can be solved if we can obtain the final
PlotRange
. The question to be asked is: How can we determine thePlotRange
of a figure while we are drawing it?
Of course, PlotRange
can be set manually, or be obtained after the fact, but that would mean something ugly like:
pr1 = -Subtract @@PlotRange[
Graphics[{FaceForm[], EdgeForm[{Thickness[0.3], Blue}],
Disk[{0, 0}], Disk[{3, 0}], Disk[{6, 0}]}]][[1]];
pr2 = -Subtract @@ PlotRange[
Graphics[{FaceForm[], EdgeForm[{Thickness[0.3], Blue}], Disk[{0, 0}]}]][[1]];
Graphics[{FaceForm[], EdgeForm[{Thickness[0.3 pr2/pr1], Blue}],
Disk[{0, 0}], Disk[{3, 0}], Disk[{6, 0}]}]
Graphics[{FaceForm[], EdgeForm[{Thickness[0.3], Blue}], Disk[{0, 0}]}]
This works, but isn't elegant.