Suppose I want a string, say "123", to fill a given rectangle, like so:
Show[Plot[x, {x, 0, 1}],
Graphics[{EdgeForm[Thick], Yellow, Rectangle[{.1, .5}, {.4, .9}]}],
Graphics[Text[Style["123", Red, Bold, 67], {.1, .5}, {-1, -1}]]]
But I hand-tuned the font size there (67) so that it would fill up the rectangle.
How would you make an arbitrary string fill up an arbitrary rectangle?
I believe that this is a known difficult problem. The best answer I could find is from John Fultz.
TextRect[text_, {{left_, bottom_}, {right_, top_}}] :=
Inset[
Pane[text, {Scaled[1], Scaled[1]},
ImageSizeAction -> "ResizeToFit", Alignment -> Center],
{left, bottom}, {Left, Bottom}, {right - left, top - bottom}]
Show[
Plot[x, {x, 0, 1}],
Graphics[{
{EdgeForm[Thick], Yellow, Rectangle[{.1, .5}, {.4, .9}]},
TextRect[Style["123", Red, Bold], {{.1, .5}, {.4, .9}}]
}]
]
Here's an alternate approach that converts the text to a texture that gets mapped to a polygon. This has the feature of stretching the text to fit the region (since it's not really text anymore.)
Show[Plot[x, {x, 0, 1}],
Graphics[{EdgeForm[Thick], Yellow, Rectangle[{.1, .5}, {.4, .9}]}],
Graphics[{Texture[ImageData[
Rasterize[Style["123", Red, Bold], "Image", RasterSize -> 300,
Background -> None]]],
Polygon[{{0.1, 0.5}, {0.4, 0.5}, {0.4, 0.9}, {0.1, 0.9}},
VertexTextureCoordinates -> {{0, 0}, {1, 0}, {1, 1}, {0, 1}}]}]]
As a function for easier comparison:
(* Render string/style s to fill a rectangle with left/bottom corner {l,b} and
right/top corner {r,t}. *)
textrect[s_, {{l_,b_},{r_,t_}}] := Graphics[{
Texture[ImageData[Rasterize[s, "Image", RasterSize->300, Background->None]]],
Polygon[{{l,b}, {r,b}, {r,t}, {l,t}},
VertexTextureCoordinates->{{0,0},{1,0},{1,1},{0,1}}]}]
The suggested solution didn't work when the Plot wasn't there, I used the PlotRange option to solve it. I wrapped it in a function; Opacity, text color, etc; should be made into options;
textBox[text_, color_, position_: {0, 0}, width_: 2, height_: 1] :=
Graphics[{
{
color, Opacity[.1],
Rectangle[position, position + {width, height},
RoundingRadius -> 0.1]
}
,
Inset[
Pane[text, {Scaled[1], Scaled[1]},
ImageSizeAction -> "ResizeToFit", Alignment -> Center],
position, {Left, Bottom}, {width, height}]
}, PlotRange ->
Transpose[{position, position + {width, height}}]];
Related
I want to plot a series of lines with one half-space filled for each line. By setting opacity to something less than 1, I want to make the overlaps stand out. What I have looks something like this:
Plot[Table[x + a, {a, 0, 5}], {x, -1/2, 1/2},
RegionFunction -> Function[{x, y}, y < 5],
Filling -> 5, FillingStyle -> Directive[Opacity[0.25]]]
This is fine. Now I want to also shade the colors for each half space in a particular way. Instead of the flat shading for each at present, say I want to shade it by the y value. I.e., if the flat shade color is blue, the shade of blue is scaled by y (0 most intense or 5 most intense doesn't matter). So at the first overlap, it automatically becomes 2y, 3y when two half-spaces overlay.
How do I do this?
You could try ParametricPlot. For example
ParametricPlot[
Table[{s, i + s/2 + t}, {i, 0, 2}], {s, 0, 1}, {t, 0, 3},
Mesh -> False, PlotStyle -> Automatic,
ColorFunctionScaling -> False,
PlotRange -> {Automatic, {0, 3}},
ColorFunction -> Function[{x, y, s, t},
Directive[Opacity[0.2], ColorData["NeonColors"][y/3]]],
AspectRatio -> 1]
Result:
If you run the following code snippet:
Manipulate[
Graphics3D[
{Cuboid[{{-1, -1, -1}, {1, 1, 1}}], Sphere[{5, 5, 5}, 1]},
ViewPoint -> {1, 1, a}, AxesOrigin -> {0,0,0}
],
{a, 1, 100}
]
and move the viewpoint from (1,1,1) to (1,1,100) with the slider you will see that after a while the objects remain fixed in size.
Questions.
1. When I move the viewpoint further away from the scene I want the objects to become smaller. How should this be done in Mathematica?
( EDIT: )
2. What is the position of the 'camera' in relation to Viewpoint?
See ViewAngle. Under "More Information", note that the default setting ViewAngle -> Automatic is effectively equivalent to ViewAngle -> All when you zoom far enough out.
You just need to add an explicit setting for ViewAngle:
Manipulate[
Graphics3D[{Cuboid[{{-1, -1, -1}, {1, 1, 1}}], Sphere[{5, 5, 5}, 1]},
ViewPoint -> {1, 1, a}, AxesOrigin -> {0, 0, 0},
ViewAngle -> 35 Degree], {a, 1, 100}]
As far as I know, the camera viewpoint really coincides with the position given by ViewPoint. Because Mathematica scales the result to fit in about the same image you don't see much changes but they are there. The perspective changes considerably. Try, for instance, to move away from a semi-transparant square and you'll see that the farther you go, the more the projection becomes an orthogonal projection:
If you want to scale your image according to distance you can use ImageSize. SphericalRegion is good to stabilize the image.
Manipulate[
vp = {1, 1, a};
Graphics3D[{Cuboid[{{-1, -1, -1}, {1, 1, 1}}], Sphere[{5, 5, 5}, 1]},
ViewPoint -> vp,
AxesOrigin -> {0, 0, 0},
SphericalRegion -> True,
ImageSize -> 500/Norm[vp]],
{a, 1, 100}
]
[animation made with some ImagePadding to keep object in the center. I stopped the animation at a = 10, the image gets pretty small after that]
According to
http://reference.wolfram.com/mathematica/ref/AxesLabel.html
it says
"By default, axes labels in two-dimensional graphics are placed at the ends of the axes. In three-dimensional graphics, they are aligned with the middles of the axes."
I wanted to put the axes labels at the end of the axes also for my 3D plots, since that makes it easy for me to see which axes is now where when I do rotations and such on the 3D objects.
I was not able to find a trick to do it. Here is an example
g=Graphics3D[
{
Cuboid[{-.1,-.1,-.1},{.1,.1,.1}],
{Red,PointSize[.03],Point[{3,0,0}]},
{Black,PointSize[.03],Point[{0,3,0}]},
{Blue,PointSize[.03],Point[{0,0,3}]}
},
AxesOrigin->{0,0,0},
PlotRange->{{-3,3},{-3,3},{-3,3}},
Axes->True,
AxesLabel->{"X","Y","Z"},
LabelStyle->Directive[Bold,Red,16],
PreserveImageOptions->False,
Ticks->None,
Boxed->False
]
Also, it says that the axes labels for 3D are supposed to be in the 'middle' of the axes.
But looking at the resulting Graphics3D, it does not look to me the labels are in the middle at all. Might be a scaling thing, not sure now, but it looks like the labels are too close to the origin.
thanks,
You could draw the labels manually, at the location of your choosing:
Graphics3D[
{ Cuboid[{-.1,-.1,-.1},{.1,.1,.1}]
, Text[Style["X", Bold, Red, 16], {3, 0, 0}]
, Text[Style["Y", Bold, Black, 16], {0, 3, 0}]
, Text[Style["Z", Bold, Blue, 16], {0, 0, 3}]
}
, AxesOrigin -> {0, 0, 0}
, PlotRange -> {{-3, 3}, {-3, 3}, {-3, 3}}
, Axes -> True
, PreserveImageOptions -> False
, Ticks -> None
, Boxed -> False
]
I have a graphical object which is moving along a trajectory. How can I make the camera follow the object?
Let's draw a planet and its satellite, with the camera following the moon from a view directed toward the Earth. For example:
a = {-3.5, 3.5};
Animate[
Show[
Graphics3D[
Sphere[3 {Cos#t, Sin#t, 0}, .5],
ViewPoint -> 3.5 {Cos#t, Sin#t, 0},
SphericalRegion -> True,
PlotRange -> {a, a, a}, Axes -> False, Boxed -> False],
myEarth],
{t, 0, 2 Pi}]
Where myEarth is another 3D Graphics (for reference).
Static vertical view:
a = {-3.5, 3.5};
Animate[
Show[
Graphics3D[
Sphere[3 {Cos#t, Sin#t, 0}, .5],
ViewPoint -> 3.5 {0,0,1},
SphericalRegion -> True,
PlotRange -> {a, a, a}, Axes -> False, Boxed -> False],
myEarth],
{t, 0, 2 Pi}]
The trick is SphericalRegion -> True, without it the image perspective "moves" from frame to frame.
Edit
With two static objects:
Since the question asks about 2D, here's how you can emulate a camera in 2D Graphics.
First, let's get the stackoverflow favicon.ico:
so = First#Import["http://sstatic.net/stackoverflow/img/favicon.ico"]
Well put this on top of some overlapping circles and make the "camera" follow the icon around by adjusting the PlotRange
Manipulate[Graphics[{
Table[Circle[{j, 0}, i], {i, 0, 1, .1}, {j, {-.5, .5}}],
Inset[so, pos, {0, 0}, .2]},
PlotRange -> {{-.5, .5}, {-.5, .5}} + pos],
{{pos, {0, 0}, ""}, {-1.4, -1}, {1.4, 1}, ControlPlacement -> Left}]
To show how it works (with out putting the above into Mathematica), we need to animate it.
Originally I chose a variable step random walk drunk = Accumulate[RandomReal[{-.1, .1}, {200, 2}]] but it was a unpredictable! So instead, we'll make the icon follow the ABC logo
drunk = Table[{1.5 Sin[t], Cos[3 t]}, {t, 0, 2 Pi, .1}];
Animate[Graphics[{
Table[Circle[{j, 0}, i], {i, 0, 1, .1}, {j, {-.5, .5}}],
Inset[so, drunk[[pos]], {0, 0}, .2]},
PlotRange -> {{-.5, .5}, {-.5, .5}} + drunk[[pos]]],
{pos, 1, Length[drunk], 1}]
I'm having problem with custom VertexRenderingFunction showing at different sizes for different graphs. An example is below, the default vertex rendering function has the desired behavior since vertices look the same in all graphs, any suggestion how to achieve that with custom vertices?
(source: yaroslavvb.com)
edges = Most[
ArrayRules[GraphData[{"Path", 5}, "AdjacencyMatrix"]]][[All, 1]];
doit[vrf_] :=
Print /# Table[
GraphPlot[Rule ### edges[[k ;;]], VertexRenderingFunction -> vrf,
VertexLabeling -> True], {k, 1, Length[edges]}];
doit[({White, EdgeForm[Black], Disk[#, .1], Black, Text[#2, #1]} &)];
doit[Automatic];
Update, 1 hour later:
Michael Pilat as usual gives the solution, here's what it looks like with (Inset[Graphics[{White, EdgeForm[Black], Disk[{0, 0}, .05], Black,
Text[#2, {0, 0}]}, ImageSize -> 25], #] &) for rendering function
(source: yaroslavvb.com)
Inset a Graphics expression with the ImageSize option to place your vertices:
GraphPlot[Rule ### edges,
VertexRenderingFunction -> (Inset[
Graphics[{White, EdgeForm[Black], Disk[{0, 0}, .05], Black,
Text[#2, {0, 0}]}, ImageSize -> 25], #] &),
VertexLabeling -> True]
ImageSize can take a variety of values from printer's points to a Scaled value.
Inset can also/instead take a size in its fourth argument, but the default setting defers to the ImageSize of the inset Graphics object, which is a little cleaner to use in this case.
Hope that helps!