GraphPlots的一致大小
更新10/27:我已经提出了在答案中实现一致规模的详细步骤。 基本上对于每个图形对象,您需要将所有填充/边距修正为0,并手动指定plotRange和imageSize,以便1)plotRange包含所有图形2)imageSize = scale * plotRange
现在仍然确定如何完成1),给出了一种适用于由点和粗线组成的图形(AbsoluteThickness)的解决方案
我在VertexRenderingFunction和“VertexCoordinates”中使用“Inset”来保证图的子图之间一致的外观。 这些子图被绘制为另一个图的顶点,使用“插入”。 有两个问题,一个是结果框没有在图形周围裁剪(即,一个顶点的图仍然放在一个大框中),另一个是大小之间存在奇怪的变化(你可以看到一个框是垂直的) 。 任何人都可以看到解决这些问题的方法?
这与前面关于如何保持顶点大小看起来相同的问题有关,尽管Michael Pilat提出的使用Inset的建议使顶点呈现的尺寸保持相同,但整体尺寸可能不同。 例如在左边的分支上,由顶点2,3组成的图相对于顶部图中的“2,3”子图展开,即使我使用绝对顶点定位
http://yaroslavvb.com/upload/bad-graph.png
(*utilities*)intersect[a_, b_] := Select[a, MemberQ[b, #] &];
induced[s_] := Select[edges, #~intersect~s == # &];
Needs["GraphUtilities`"];
subgraphs[
verts_] := (gr =
Rule @@@ Select[edges, (Intersection[#, verts] == #) &];
Sort /@ WeakComponents[gr~Join~(# -> # & /@ verts)]);
(*graph*)
gname = {"Grid", {3, 3}};
edges = GraphData[gname, "EdgeIndices"];
nodes = Union[Flatten[edges]];
AppendTo[edges, #] & /@ ({#, #} & /@ nodes);
vcoords = Thread[nodes -> GraphData[gname, "VertexCoordinates"]];
(*decompose*)
edgesOuter = {};
pr[_, _, {}] := None;
pr[root_, elim_,
remain_] := (If[root != {}, AppendTo[edgesOuter, root -> remain]];
pr[remain, intersect[Rest[elim], #], #] & /@
subgraphs[Complement[remain, {First[elim]}]];);
pr[{}, {4, 5, 6, 1, 8, 2, 3, 7, 9}, nodes];
(*visualize*)
vrfInner =
Inset[Graphics[{White, EdgeForm[Black], Disk[{0, 0}, .05], Black,
Text[#2, {0, 0}]}, ImageSize -> 15], #] &;
vrfOuter =
Inset[GraphPlot[Rule @@@ induced[#2],
VertexRenderingFunction -> vrfInner,
VertexCoordinateRules -> vcoords, SelfLoopStyle -> None,
Frame -> True, ImageSize -> 100], #] &;
TreePlot[edgesOuter, Automatic, nodes,
EdgeRenderingFunction -> ({Red, Arrow[#1, 0.2]} &),
VertexRenderingFunction -> vrfOuter, ImageSize -> 500]
这里是另一个例子,与以前相同的问题,但相对尺度的差异更明显。 目标是让第二张照片中的部分与第一张照片中的部分精确匹配。
http://yaroslavvb.com/upload/bad-plot2.png
(* Visualize tree decomposition of a 3x3 grid *)
inducedGraph[set_] := Select[edges, # [Subset] set &];
Subset[a_, b_] := (a [Intersection] b == a);
graphName = {"Grid", {3, 3}};
edges = GraphData[graphName, "EdgeIndices"];
vars = Range[GraphData[graphName, "VertexCount"]];
vcoords = Thread[vars -> GraphData[graphName, "VertexCoordinates"]];
plotHighlight[verts_, color_] := Module[{vpos, coords},
vpos =
Position[Range[GraphData[graphName, "VertexCount"]],
Alternatives @@ verts];
coords = Extract[GraphData[graphName, "VertexCoordinates"], vpos];
If[coords != {}, AppendTo[coords, First[coords] + .002]];
Graphics[{color, CapForm["Round"], JoinForm["Round"],
Thickness[.2], Opacity[.3], Line[coords]}]];
jedges = {{{1, 2, 4}, {2, 4, 5, 6}}, {{2, 3, 6}, {2, 4, 5, 6}}, {{4,
5, 6}, {2, 4, 5, 6}}, {{4, 5, 6}, {4, 5, 6, 8}}, {{4, 7, 8}, {4,
5, 6, 8}}, {{6, 8, 9}, {4, 5, 6, 8}}};
jnodes = Union[Flatten[jedges, 1]];
SeedRandom[1]; colors =
RandomChoice[ColorData["WebSafe", "ColorList"], Length[jnodes]];
bags = MapIndexed[plotHighlight[#, bc[#] = colors[[First[#2]]]] &,
jnodes];
Show[bags~
Join~{GraphPlot[Rule @@@ edges, VertexCoordinateRules -> vcoords,
VertexLabeling -> True]}, ImageSize -> Small]
bagCentroid[bag_] := Mean[bag /. vcoords];
findExtremeBag[vec_] := (
vertList = First /@ vcoords;
coordList = Last /@ vcoords;
extremePos =
First[Ordering[jnodes, 1,
bagCentroid[#1].vec > bagCentroid[#2].vec &]];
jnodes[[extremePos]]
);
extremeDirs = {{1, 1}, {1, -1}, {-1, 1}, {-1, -1}};
extremeBags = findExtremeBag /@ extremeDirs;
extremePoses = bagCentroid /@ extremeBags;
vrfOuter =
Inset[Show[plotHighlight[#2, bc[#2]],
GraphPlot[Rule @@@ inducedGraph[#2],
VertexCoordinateRules -> vcoords, SelfLoopStyle -> None,
VertexLabeling -> True], ImageSize -> 100], #] &;
GraphPlot[Rule @@@ jedges, VertexRenderingFunction -> vrfOuter,
EdgeRenderingFunction -> ({Red, Arrowheads[0], Arrow[#1, 0]} &),
ImageSize -> 500,
VertexCoordinateRules -> Thread[Thread[extremeBags -> extremePoses]]]
欢迎任何其他建议让美观的图形操作的可视化。
以下是实现精确控制图形对象相对比例所需的步骤。
要实现一致的缩放比例,需要明确指定输入坐标范围(常规坐标)和输出坐标范围(绝对坐标)。 常规坐标范围取决于PlotRange
, PlotRangePadding
(可能还有其他选项?)。 绝对坐标范围取决于ImageSize
, ImagePadding
(可能还有其他选项?)。 对于GraphPlot
,指定PlotRange
和ImageSize
就足够了。
要创建以预定比例呈现的Graphics对象,需要确定PlotRange
需要完全包含对象,相应的ImageSize
并返回Graphics
对象,并指定这些设置。 为了在涉及粗线时找出必要的PlotRange
,处理AbsoluteThickness
更容易,称之为abs
。 要完全包含这些行,可以采用包含端点的最小PlotRange
,然后用abs / 2偏移最小x和最大y边界,并用(abs / 2 + 1)偏移最大x和最小y边界。 请注意,这些是输出坐标。
组合多个经过scale-calibrated
图形对象时,需要重新计算PlotRange/ImageSize
并为组合的Graphics对象显式设置它们。
要将scale-calibrated
对象插入到GraphPlot
您需要确保用于自动GraphPlot
定位的坐标位于相同的范围内。 为此,您可以选择几个角落节点,手动修复它们的位置,然后让剩余的自动定位完成。
原始Line
/连接JoinedCurve
/ FilledCurve
根据线是否(几乎)共线来渲染连接/线帽,因此需要手动检测共线性。
使用这种方法,渲染图像的宽度应等于
(inputPlotRange*scale + 1) + lineThickness*scale + 1
第一个额外的1
是为了避免“fencepost错误”,第二个额外的1是在右边添加所需的额外像素,以确保粗线不会被截断
我已经通过对组合Show
进行Rasterize
并对使用Texture
映射的对象栅格化并使用Orthographic
投影进行查看来验证此公式,并且它与预测结果相匹配。 在对象上执行“复制/粘贴” Inset
到GraphPlot
,然后进行光栅化,我得到的图像比预测的要薄一个像素。
http://yaroslavvb.com/upload/graphPlots.png
(**** Note, this uses JoinedCurve and Texture which are Mathematica 8 primitives.
In Mathematica 7, JoinedCurve is not needed and can be removed *)
(** Global variables **)
scale = 50;
lineThickness = 1/2; (* line thickness in regular coordinates *)
(** Global utilities **)
(* test if 3 points are collinear, needed to work around difference
in how colinear Line endpoints are rendered *)
collinear[points_] :=
Length[points] == 3 && (Det[Transpose[points]~Append~{1, 1, 1}] == 0)
(* tales list of point coordinates, returns plotRange bounding box,
uses global "scale" and "lineThickness" to get bounding box *)
getPlotRange[lst_] := (
{xs, ys} = Transpose[lst];
(* two extra 1/
scale offsets needed for exact match *)
{{Min[xs] -
lineThickness/2,
Max[xs] + lineThickness/2 + 1/scale}, {Min[ys] -
lineThickness/2 - 1/scale, Max[ys] + lineThickness/2}}
);
(* Gets image size for given plot range *)
getImageSize[{{xmin_, xmax_}, {ymin_, ymax_}}] := (
imsize = scale*{xmax - xmin, ymax - ymin} + {1, 1}
);
(* converts plot range to vertices of rectangle *)
pr2verts[{{xmin_, xmax_}, {ymin_, ymax_}}] := {{xmin, ymin}, {xmax,
ymin}, {xmax, ymax}, {xmin, ymax}};
(* lifts two dimensional coordinates into 3d *)
lift[h_, coords_] := Append[#, h] & /@ coords
(* convert Raster object to array specification of texture *)
raster2texture[raster_] := Reverse[raster[[1, 1]]/255]
Subset[a_, b_] := (a [Intersection] b == a);
inducedGraph[set_] := Select[edges, # [Subset] set &];
values[dict_] := Map[#[[-1]] &, DownValues[dict]];
(** Graph Specific Stuff *)
graphName = {"Grid", {3, 3}};
verts = Range[GraphData[graphName, "VertexCount"]];
edges = GraphData[graphName, "EdgeIndices"];
vcoords = Thread[verts -> GraphData[graphName, "VertexCoordinates"]];
jedges = {{{1, 2, 4}, {2, 4, 5, 6}}, {{2, 3, 6}, {2, 4, 5, 6}}, {{4,
5, 6}, {2, 4, 5, 6}}, {{4, 5, 6}, {4, 5, 6, 8}}, {{4, 7, 8}, {4,
5, 6, 8}}, {{6, 8, 9}, {4, 5, 6, 8}}};
jnodes = Union[Flatten[jedges, 1]];
(* Generate diagram with explicit PlotRange,ImageSize and
AbsoluteThickness *)
plotHL[verts_, color_] := (
coords = verts /. vcoords;
obj = JoinedCurve[Line[coords],
CurveClosed -> Not[collinear[coords]]];
(* Figure out PlotRange and ImageSize needed to respect scale *)
pr = getPlotRange[verts /. vcoords];
{{xmin, xmax}, {ymin, ymax}} = pr;
imsize = scale*{xmax - xmin, ymax - ymin};
lineForm = {Opacity[.3], color, JoinForm["Round"],
CapForm["Round"], AbsoluteThickness[scale*lineThickness]};
g = Graphics[{Directive[lineForm], obj}];
gg = GraphPlot[Rule @@@ inducedGraph[verts],
VertexCoordinateRules -> vcoords];
Show[g, gg, PlotRange -> pr, ImageSize -> imsize]
);
(* Initialize all graph plot images *)
SeedRandom[1]; colors =
RandomChoice[ColorData["WebSafe", "ColorList"], Length[jnodes]];
Clear[bags];
MapThread[(bags[#1] = plotHL[#1, #2]) &, {jnodes, colors}];
(** Ploting parent graph of subgraphs **)
(* figure out coordinates of subgraphs close to edges of bounding
box, use them to anchor parent GraphPlot *)
bagCentroid[bag_] := Mean[bag /. vcoords];
findExtremeBag[vec_] := (vertList = First /@ vcoords;
coordList = Last /@ vcoords;
extremePos =
First[Ordering[jnodes, 1,
bagCentroid[#1].vec > bagCentroid[#2].vec &]];
jnodes[[extremePos]]);
extremeDirs = {{1, 1}, {1, -1}, {-1, 1}, {-1, -1}};
extremeBags = findExtremeBag /@ extremeDirs;
extremePoses = bagCentroid /@ extremeBags;
(* figure out new plot range needed to contain all objects *)
fullPR = getPlotRange[verts /. vcoords];
fullIS = getImageSize[fullPR];
(*** Show bags together merged ***)
image1 =
Show[values[bags], PlotRange -> fullPR, ImageSize -> fullIS]
(*** Show bags as vertices of another GraphPlot ***)
GraphPlot[
Rule @@@ jedges,
EdgeRenderingFunction -> ({Gray, Thick, Arrowheads[.05],
Arrow[#1, 0.22]} &),
VertexCoordinateRules ->
Thread[Thread[extremeBags -> extremePoses]],
VertexRenderingFunction -> (Inset[bags[#2], #] &),
PlotRange -> fullPR,
ImageSize -> 3*fullIS
]
(*** Show bags as 3d slides ***)
makeSlide[graphics_, pr_, h_] := (
Graphics3D[{
Texture[raster2texture[Rasterize[graphics, Background -> None]]],
EdgeForm[None],
Polygon[lift[h, pr2verts[pr]],
VertexTextureCoordinates -> pr2verts[{{0, 1}, {0, 1}}]]
}]
)
yoffset = 1/2;
slides = MapIndexed[
makeSlide[bags[#], getPlotRange[# /. vcoords],
yoffset*First[#2]] &, jnodes];
Show[slides, ImageSize -> 3*fullIS]
(*** Show 3d slides in orthographic projection ***)
image2 =
Show[slides, ViewPoint -> {0, 0, Infinity}, ImageSize -> fullIS,
Boxed -> False]
(*** Check that 3d and 2d images rasterize to identical resolution ***)
Dimensions[Rasterize[image1][[1, 1]]] ==
Dimensions[Rasterize[image2][[1, 1]]]
好的,在你对我之前的回答(这是一种不同的方法)的评论中,你说问题是GraphPlot / Inset / PlotRange之间的交互作用。 如果您没有为Inset
指定大小,则它会从Inset
Graphics
对象的ImageSize
继承它的大小。
这是我对第一个例子中最后一节的编辑,这次考虑了Inset
图形的大小。
(*visualize*)
vrfInner = Inset[Graphics[{White, EdgeForm[Black], Disk[{0, 0}, .05], Black,
Text[#2, {0, 0}]}, ImageSize -> 15], #, Center] &;
vrfOuter = Module[{edges = Rule @@@ induced[#2], prange, psize},
prange = Union /@ Transpose[Union[Flatten[List @@@ edges]] /. vcoords];
prange = {Min[#] - .5, Max[#] + .5} & /@ prange;
psize = Subtract @@@ Reverse /@ prange;
Inset[GraphPlot[edges, VertexRenderingFunction -> vrfInner,
VertexCoordinateRules -> vcoords, SelfLoopStyle -> None,
Frame -> True, ImageSize -> 100, PlotRange -> prange,
PlotRangePadding -> None], #, Center, Scaled[psize {.05, .04}],
Background -> None ]] &;
TreePlot[edgesOuter, Automatic, nodes,
EdgeRenderingFunction -> ({Red, Arrow[#1, 0.25]} &),
VertexRenderingFunction -> vrfOuter, ImageSize -> 500]
因为外部图形的大小和布局发生了变化{.05, .04}
因此{.05, .04}
将不得不进行修改...为了使整个事情自动化,您可能需要一种很好的方式让内部和外部图形对象互相检查...
您可以通过更改vrfOuter来修复您的第一个示例,如下所示:
vrfOuter =
Inset[
Framed@GraphPlot[
Rule@@@induced[#2],
VertexRenderingFunction -> vrfInner,
VertexCoordinateRules -> vcoords,
SelfLoopStyle -> None,
ImageSize -> {100, 100},
AspectRatio -> 1,
PlotRange -> {{1, 3}, {1, 3}}
],
#
] &;
我删除了框架 - >全部选项,并添加了一个包装呼叫帧。 这是因为我发现我无法充分控制前者产生的边框外边缘。 我可能在某处丢失了一些选项,但Framed以我想要的方式工作而不会大惊小怪。
我为ImageSize选项添加了一个明确的高度。 如果没有它,Mathematica会尝试使用一些算法来选择一个高度,这些算法通常会产生令人满意的结果,但有时(如此处)会变得困惑。
为了同样的原因,我添加了AspectRatio选项 - Mathematica试图选择一个“令人愉快的”宽高比(通常是黄金比例),但我们不希望这样。
我添加了PlotRange选项以确保每个子图都使用相同的坐标系。 没有它,Mathematica通常会选择一个显示所有节点的最小范围。
结果如下所示。 我把它作为练习给读者调整箭头,边距等;)
编辑:添加PlotRange选项以回应@Yaroslav Bulatov的评论
链接地址: http://www.djcxy.com/p/35493.html