mirror of
https://github.com/rizinorg/cutter.git
synced 2024-12-18 10:56:11 +00:00
Rewrite part of graph layout code (#2207)
* Rewrite node placement and edge routing parts of graph layout code * Document the high level structure of layout algorithm * Tighter layout and less edge crossings * Better worst case memory and CPU usage
This commit is contained in:
parent
1e9b82839e
commit
54ecc33ca9
@ -762,7 +762,7 @@ WARNINGS = YES
|
||||
# will automatically be disabled.
|
||||
# The default value is: YES.
|
||||
|
||||
WARN_IF_UNDOCUMENTED = YES
|
||||
WARN_IF_UNDOCUMENTED = NO
|
||||
|
||||
# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for
|
||||
# potential errors in the documentation, such as not documenting some parameters
|
||||
@ -950,7 +950,7 @@ EXAMPLE_RECURSIVE = NO
|
||||
# that contain images that are to be included in the documentation (see the
|
||||
# \image command).
|
||||
|
||||
IMAGE_PATH =
|
||||
IMAGE_PATH = doxygen-images/graph_grid_layout
|
||||
|
||||
# The INPUT_FILTER tag can be used to specify a program that doxygen should
|
||||
# invoke to filter for each input file. Doxygen will invoke the filter program
|
||||
@ -1127,7 +1127,7 @@ IGNORE_PREFIX =
|
||||
# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output
|
||||
# The default value is: YES.
|
||||
|
||||
GENERATE_HTML = NO
|
||||
GENERATE_HTML = YES
|
||||
|
||||
# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a
|
||||
# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
|
||||
|
664
docs/doxygen-images/graph_grid_layout/graph_grid.svg
Normal file
664
docs/doxygen-images/graph_grid_layout/graph_grid.svg
Normal file
@ -0,0 +1,664 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="95mm"
|
||||
height="60mm"
|
||||
viewBox="0 0 95 60"
|
||||
version="1.1"
|
||||
id="svg8"
|
||||
inkscape:version="1.0 (4035a4fb49, 2020-05-01)"
|
||||
sodipodi:docname="grid.svg">
|
||||
<defs
|
||||
id="defs2">
|
||||
<marker
|
||||
inkscape:isstock="true"
|
||||
style="overflow:visible"
|
||||
id="marker2306"
|
||||
refX="0"
|
||||
refY="0"
|
||||
orient="auto"
|
||||
inkscape:stockid="Arrow2Mend">
|
||||
<path
|
||||
transform="scale(-0.6)"
|
||||
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
|
||||
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
|
||||
id="path2304" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:collect="always"
|
||||
inkscape:stockid="Arrow2Mend"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="marker1726"
|
||||
style="overflow:visible"
|
||||
inkscape:isstock="true">
|
||||
<path
|
||||
id="path1724"
|
||||
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
|
||||
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
|
||||
transform="scale(-0.6)" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow2Mend"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="marker1388"
|
||||
style="overflow:visible"
|
||||
inkscape:isstock="true">
|
||||
<path
|
||||
id="path1386"
|
||||
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
|
||||
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
|
||||
transform="scale(-0.6)" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:collect="always"
|
||||
inkscape:isstock="true"
|
||||
style="overflow:visible"
|
||||
id="Arrow2Mend"
|
||||
refX="0"
|
||||
refY="0"
|
||||
orient="auto"
|
||||
inkscape:stockid="Arrow2Mend">
|
||||
<path
|
||||
transform="scale(-0.6)"
|
||||
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
|
||||
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
|
||||
id="path1115" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:isstock="true"
|
||||
style="overflow:visible"
|
||||
id="Arrow2Lend"
|
||||
refX="0"
|
||||
refY="0"
|
||||
orient="auto"
|
||||
inkscape:stockid="Arrow2Lend">
|
||||
<path
|
||||
transform="matrix(-1.1,0,0,-1.1,-1.1,0)"
|
||||
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
|
||||
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
|
||||
id="path1109" />
|
||||
</marker>
|
||||
<marker
|
||||
style="overflow:visible"
|
||||
refY="0"
|
||||
refX="0"
|
||||
orient="auto"
|
||||
inkscape:stockid="Arrow2Sstart"
|
||||
inkscape:isstock="true"
|
||||
id="Arrow2Sstart">
|
||||
<path
|
||||
transform="matrix(0.3,0,0,0.3,-0.69,0)"
|
||||
style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-opacity:1"
|
||||
id="Arrow2SstartPath"
|
||||
d="M 8.72,4.03 -2.21,0.02 8.72,-4 c -1.75,2.37 -1.74,5.62 0,8.03 z" />
|
||||
</marker>
|
||||
<marker
|
||||
style="overflow:visible"
|
||||
refY="0"
|
||||
refX="0"
|
||||
orient="auto"
|
||||
inkscape:stockid="Arrow2Send"
|
||||
inkscape:isstock="true"
|
||||
id="Arrow2Send">
|
||||
<path
|
||||
transform="matrix(-0.3,0,0,-0.3,0.69,0)"
|
||||
style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-opacity:1"
|
||||
id="Arrow2SendPath"
|
||||
d="M 8.72,4.03 -2.21,0.02 8.72,-4 c -1.75,2.37 -1.74,5.62 0,8.03 z" />
|
||||
</marker>
|
||||
<marker
|
||||
id="Arrow2Sstart-1"
|
||||
inkscape:isstock="true"
|
||||
inkscape:stockid="Arrow2Sstart"
|
||||
orient="auto"
|
||||
refX="0"
|
||||
refY="0"
|
||||
style="overflow:visible">
|
||||
<path
|
||||
d="M 8.72,4.03 -2.21,0.02 8.72,-4 c -1.75,2.37 -1.74,5.62 0,8.03 z"
|
||||
id="Arrow2SstartPath-8"
|
||||
style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-opacity:1"
|
||||
transform="matrix(0.3,0,0,0.3,-0.69,0)" />
|
||||
</marker>
|
||||
<marker
|
||||
id="Arrow2Send-7"
|
||||
inkscape:isstock="true"
|
||||
inkscape:stockid="Arrow2Send"
|
||||
orient="auto"
|
||||
refX="0"
|
||||
refY="0"
|
||||
style="overflow:visible">
|
||||
<path
|
||||
d="M 8.72,4.03 -2.21,0.02 8.72,-4 c -1.75,2.37 -1.74,5.62 0,8.03 z"
|
||||
id="Arrow2SendPath-9"
|
||||
style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-opacity:1"
|
||||
transform="matrix(-0.3,0,0,-0.3,0.69,0)" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:isstock="true"
|
||||
style="overflow:visible"
|
||||
id="marker1726-0"
|
||||
refX="0"
|
||||
refY="0"
|
||||
orient="auto"
|
||||
inkscape:stockid="Arrow2Mend">
|
||||
<path
|
||||
transform="scale(-0.6)"
|
||||
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
|
||||
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
|
||||
id="path1724-2" />
|
||||
</marker>
|
||||
<marker
|
||||
id="Arrow2Sstart-7"
|
||||
inkscape:isstock="true"
|
||||
inkscape:stockid="Arrow2Sstart"
|
||||
orient="auto"
|
||||
refX="0"
|
||||
refY="0"
|
||||
style="overflow:visible">
|
||||
<path
|
||||
d="M 8.72,4.03 -2.21,0.02 8.72,-4 c -1.75,2.37 -1.74,5.62 0,8.03 z"
|
||||
id="Arrow2SstartPath-5"
|
||||
style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-opacity:1"
|
||||
transform="matrix(0.3,0,0,0.3,-0.69,0)" />
|
||||
</marker>
|
||||
<marker
|
||||
id="Arrow2Send-9"
|
||||
inkscape:isstock="true"
|
||||
inkscape:stockid="Arrow2Send"
|
||||
orient="auto"
|
||||
refX="0"
|
||||
refY="0"
|
||||
style="overflow:visible">
|
||||
<path
|
||||
d="M 8.72,4.03 -2.21,0.02 8.72,-4 c -1.75,2.37 -1.74,5.62 0,8.03 z"
|
||||
id="Arrow2SendPath-2"
|
||||
style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-opacity:1"
|
||||
transform="matrix(-0.3,0,0,-0.3,0.69,0)" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow2Mend"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="marker1726-0-3"
|
||||
style="overflow:visible"
|
||||
inkscape:isstock="true">
|
||||
<path
|
||||
id="path1724-2-6"
|
||||
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
|
||||
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
|
||||
transform="scale(-0.6)" />
|
||||
</marker>
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
fit-margin-bottom="0"
|
||||
fit-margin-right="0"
|
||||
fit-margin-left="0"
|
||||
fit-margin-top="0"
|
||||
inkscape:guide-bbox="true"
|
||||
showguides="true"
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="4"
|
||||
inkscape:cx="233.18761"
|
||||
inkscape:cy="199.03882"
|
||||
inkscape:document-units="mm"
|
||||
inkscape:current-layer="layer1"
|
||||
inkscape:document-rotation="0"
|
||||
showgrid="false"
|
||||
inkscape:window-width="2560"
|
||||
inkscape:window-height="1389"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1" />
|
||||
<metadata
|
||||
id="metadata5">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
transform="translate(-28.381867,-41.18436)"
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1">
|
||||
<text
|
||||
id="text888"
|
||||
y="25.535202"
|
||||
x="31.039022"
|
||||
style="font-style:normal;font-weight:normal;font-size:10.5833px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583"
|
||||
xml:space="preserve"><tspan
|
||||
style="stroke-width:0.264583"
|
||||
y="25.535202"
|
||||
x="31.039022"
|
||||
id="tspan886"
|
||||
sodipodi:role="line"></tspan></text>
|
||||
<text
|
||||
id="text892"
|
||||
y="54.462158"
|
||||
x="39.601284"
|
||||
style="font-size:2.82223px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;stroke-width:0.264583"
|
||||
xml:space="preserve"><tspan
|
||||
style="stroke-width:0.264583"
|
||||
y="54.462158"
|
||||
x="39.601284"
|
||||
id="tspan890"
|
||||
sodipodi:role="line"></tspan><tspan
|
||||
id="tspan894"
|
||||
style="stroke-width:0.264583"
|
||||
y="57.989944"
|
||||
x="39.601284"
|
||||
sodipodi:role="line">0</tspan></text>
|
||||
<text
|
||||
id="text898"
|
||||
y="57.95274"
|
||||
x="54.8783"
|
||||
style="font-size:2.82223px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;stroke-width:0.264583"
|
||||
xml:space="preserve"><tspan
|
||||
style="stroke-width:0.264583"
|
||||
y="57.95274"
|
||||
x="54.8783"
|
||||
id="tspan896"
|
||||
sodipodi:role="line">1</tspan></text>
|
||||
<text
|
||||
id="text902"
|
||||
y="53.987171"
|
||||
x="46.333523"
|
||||
style="font-size:2.82223px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;stroke-width:0.264583"
|
||||
xml:space="preserve"><tspan
|
||||
style="stroke-width:0.264583"
|
||||
y="53.987171"
|
||||
x="46.333523"
|
||||
id="tspan900"
|
||||
sodipodi:role="line">0</tspan></text>
|
||||
<text
|
||||
id="text906"
|
||||
y="53.949966"
|
||||
x="62.174358"
|
||||
style="font-size:2.82223px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;stroke-width:0.264583"
|
||||
xml:space="preserve"><tspan
|
||||
style="stroke-width:0.264583"
|
||||
y="53.949966"
|
||||
x="62.174358"
|
||||
id="tspan904"
|
||||
sodipodi:role="line">1</tspan></text>
|
||||
<text
|
||||
id="text910"
|
||||
y="57.989944"
|
||||
x="69.638489"
|
||||
style="font-size:2.82223px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;stroke-width:0.264583"
|
||||
xml:space="preserve"><tspan
|
||||
style="stroke-width:0.264583"
|
||||
y="57.989944"
|
||||
x="69.638489"
|
||||
id="tspan908"
|
||||
sodipodi:role="line">2</tspan></text>
|
||||
<text
|
||||
id="text914"
|
||||
y="53.987171"
|
||||
x="77.340141"
|
||||
style="font-size:2.82223px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;stroke-width:0.264583"
|
||||
xml:space="preserve"><tspan
|
||||
style="stroke-width:0.264583"
|
||||
y="53.987171"
|
||||
x="77.340141"
|
||||
id="tspan912"
|
||||
sodipodi:role="line">2</tspan></text>
|
||||
<text
|
||||
id="text918"
|
||||
y="57.989944"
|
||||
x="84.606102"
|
||||
style="font-size:2.82223px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;stroke-width:0.264583"
|
||||
xml:space="preserve"><tspan
|
||||
style="stroke-width:0.264583"
|
||||
y="57.989944"
|
||||
x="84.606102"
|
||||
id="tspan916"
|
||||
sodipodi:role="line">3</tspan></text>
|
||||
<text
|
||||
id="text922"
|
||||
y="53.987171"
|
||||
x="92.408722"
|
||||
style="font-size:2.82223px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;stroke-width:0.264583"
|
||||
xml:space="preserve"><tspan
|
||||
style="stroke-width:0.264583"
|
||||
y="53.987171"
|
||||
x="92.408722"
|
||||
id="tspan920"
|
||||
sodipodi:role="line">3</tspan></text>
|
||||
<text
|
||||
id="text926"
|
||||
y="62.487526"
|
||||
x="34.014694"
|
||||
style="font-size:2.82223px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;stroke-width:0.264583"
|
||||
xml:space="preserve"><tspan
|
||||
style="stroke-width:0.264583"
|
||||
y="62.487526"
|
||||
x="34.014694"
|
||||
id="tspan924"
|
||||
sodipodi:role="line">0</tspan></text>
|
||||
<text
|
||||
id="text930"
|
||||
y="69.829063"
|
||||
x="30.262985"
|
||||
style="font-size:2.82223px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;stroke-width:0.264583"
|
||||
xml:space="preserve"><tspan
|
||||
style="stroke-width:0.264583"
|
||||
y="69.829063"
|
||||
x="30.262985"
|
||||
id="tspan928"
|
||||
sodipodi:role="line">0</tspan></text>
|
||||
<text
|
||||
id="text934"
|
||||
y="77.488907"
|
||||
x="34.08773"
|
||||
style="font-size:2.82223px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;stroke-width:0.264583"
|
||||
xml:space="preserve"><tspan
|
||||
style="stroke-width:0.264583"
|
||||
y="77.488907"
|
||||
x="34.08773"
|
||||
id="tspan932"
|
||||
sodipodi:role="line">1</tspan></text>
|
||||
<text
|
||||
id="text938"
|
||||
y="85.418694"
|
||||
x="30.336021"
|
||||
style="font-size:2.82223px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;stroke-width:0.264583"
|
||||
xml:space="preserve"><tspan
|
||||
style="stroke-width:0.264583"
|
||||
y="85.418694"
|
||||
x="30.336021"
|
||||
id="tspan936"
|
||||
sodipodi:role="line">1</tspan></text>
|
||||
<text
|
||||
id="text942"
|
||||
y="92.507507"
|
||||
x="34.109779"
|
||||
style="font-size:2.82223px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;stroke-width:0.264583"
|
||||
xml:space="preserve"><tspan
|
||||
style="stroke-width:0.264583"
|
||||
y="92.507507"
|
||||
x="34.109779"
|
||||
id="tspan940"
|
||||
sodipodi:role="line">2</tspan></text>
|
||||
<text
|
||||
id="text958"
|
||||
y="53.987171"
|
||||
x="104.5532"
|
||||
style="font-size:2.82223px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;stroke-width:0.264583"
|
||||
xml:space="preserve"><tspan
|
||||
style="stroke-width:0.264583"
|
||||
y="53.987171"
|
||||
x="104.5532"
|
||||
id="tspan956"
|
||||
sodipodi:role="line">column</tspan></text>
|
||||
<text
|
||||
id="text962"
|
||||
y="57.442863"
|
||||
x="104.42841"
|
||||
style="font-size:2.82223px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;stroke-width:0.264583"
|
||||
xml:space="preserve"><tspan
|
||||
style="stroke-width:0.264583"
|
||||
y="57.442863"
|
||||
x="104.42841"
|
||||
id="tspan960"
|
||||
sodipodi:role="line">edge column</tspan></text>
|
||||
<text
|
||||
transform="rotate(90)"
|
||||
id="text966"
|
||||
y="-29.273321"
|
||||
x="43.529709"
|
||||
style="font-size:2.82223px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;stroke-width:0.264583"
|
||||
xml:space="preserve"><tspan
|
||||
style="stroke-width:0.264583"
|
||||
y="-29.273321"
|
||||
x="43.529709"
|
||||
id="tspan964"
|
||||
sodipodi:role="line">row</tspan></text>
|
||||
<text
|
||||
transform="rotate(90)"
|
||||
id="text970"
|
||||
y="-34.055241"
|
||||
x="43.230721"
|
||||
style="font-size:2.82223px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;stroke-width:0.264583"
|
||||
xml:space="preserve"><tspan
|
||||
style="stroke-width:0.264583"
|
||||
y="-34.055241"
|
||||
x="43.230721"
|
||||
id="tspan968"
|
||||
sodipodi:role="line">edge row</tspan></text>
|
||||
<rect
|
||||
inkscape:tile-y0="29.999999"
|
||||
inkscape:tile-x0="29.999999"
|
||||
style="fill:none;stroke:#000000;stroke-width:0.0930854;stroke-miterlimit:4;stroke-dasharray:none;paint-order:normal"
|
||||
id="use986"
|
||||
width="64.899994"
|
||||
height="4.9000001"
|
||||
x="38.048389"
|
||||
y="59.010197" />
|
||||
<rect
|
||||
inkscape:tile-y0="29.999999"
|
||||
inkscape:tile-x0="29.999999"
|
||||
style="fill:none;stroke:#000000;stroke-width:0.0930854;stroke-miterlimit:4;stroke-dasharray:none;paint-order:normal"
|
||||
id="use988"
|
||||
width="64.899994"
|
||||
height="4.9000001"
|
||||
x="38.048389"
|
||||
y="74.010193" />
|
||||
<rect
|
||||
inkscape:tile-y0="29.999999"
|
||||
inkscape:tile-x0="29.999999"
|
||||
style="fill:none;stroke:#000000;stroke-width:0.0930854;stroke-miterlimit:4;stroke-dasharray:none;paint-order:normal"
|
||||
id="use990"
|
||||
width="64.899994"
|
||||
height="4.9000001"
|
||||
x="38.048389"
|
||||
y="93.170723" />
|
||||
<rect
|
||||
y="-102.94839"
|
||||
x="59.010201"
|
||||
height="4.9000001"
|
||||
width="39.061729"
|
||||
id="use986-2"
|
||||
style="fill:none;stroke:#000000;stroke-width:0.068298;stroke-miterlimit:4;stroke-dasharray:none;paint-order:normal"
|
||||
inkscape:tile-x0="29.999999"
|
||||
inkscape:tile-y0="29.999999"
|
||||
transform="rotate(90)" />
|
||||
<rect
|
||||
y="-87.948387"
|
||||
x="59.010201"
|
||||
height="4.9000001"
|
||||
width="39.061729"
|
||||
id="use988-9"
|
||||
style="fill:none;stroke:#000000;stroke-width:0.068298;stroke-miterlimit:4;stroke-dasharray:none;paint-order:normal"
|
||||
inkscape:tile-x0="29.999999"
|
||||
inkscape:tile-y0="29.999999"
|
||||
transform="rotate(90)" />
|
||||
<rect
|
||||
y="-72.948387"
|
||||
x="59.010201"
|
||||
height="4.9000001"
|
||||
width="39.061729"
|
||||
id="use990-1"
|
||||
style="fill:none;stroke:#000000;stroke-width:0.068298;stroke-miterlimit:4;stroke-dasharray:none;paint-order:normal"
|
||||
inkscape:tile-x0="29.999999"
|
||||
inkscape:tile-y0="29.999999"
|
||||
transform="rotate(90)" />
|
||||
<rect
|
||||
y="-57.948387"
|
||||
x="59.010201"
|
||||
height="4.9000001"
|
||||
width="39.061729"
|
||||
id="use992-2"
|
||||
style="fill:none;stroke:#000000;stroke-width:0.068298;stroke-miterlimit:4;stroke-dasharray:none;paint-order:normal"
|
||||
inkscape:tile-x0="29.999999"
|
||||
inkscape:tile-y0="29.999999"
|
||||
transform="rotate(90)" />
|
||||
<rect
|
||||
y="-42.948387"
|
||||
x="59.010201"
|
||||
height="4.9000001"
|
||||
width="39.061729"
|
||||
id="use994-7"
|
||||
style="fill:none;stroke:#000000;stroke-width:0.068298;stroke-miterlimit:4;stroke-dasharray:none;paint-order:normal"
|
||||
inkscape:tile-x0="29.999999"
|
||||
inkscape:tile-y0="29.999999"
|
||||
transform="rotate(90)" />
|
||||
<rect
|
||||
y="64.578438"
|
||||
x="58.635674"
|
||||
height="8.5201521"
|
||||
width="23.725426"
|
||||
id="rect1026"
|
||||
style="fill:none;stroke:#000000;stroke-width:0.3;stroke-miterlimit:4;stroke-dasharray:none;paint-order:normal" />
|
||||
<rect
|
||||
style="fill:none;stroke:#000000;stroke-width:0.3;stroke-miterlimit:4;stroke-dasharray:none;paint-order:normal"
|
||||
id="rect1026-0"
|
||||
width="23.725426"
|
||||
height="12.675932"
|
||||
x="43.635674"
|
||||
y="79.704514" />
|
||||
<rect
|
||||
y="79.54509"
|
||||
x="73.635674"
|
||||
height="8.5201521"
|
||||
width="23.725426"
|
||||
id="rect1026-0-9"
|
||||
style="fill:none;stroke:#000000;stroke-width:0.3;stroke-miterlimit:4;stroke-dasharray:none;paint-order:normal" />
|
||||
<path
|
||||
sodipodi:nodetypes="ccccc"
|
||||
d="m 69.327077,73.163441 v 3.205877 H 55.388909 v 0 3.30445"
|
||||
style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-end:url(#Arrow2Mend)"
|
||||
id="path1085" />
|
||||
<path
|
||||
sodipodi:nodetypes="cccc"
|
||||
id="path1384"
|
||||
d="m 71.333422,73.07529 v 3.336512 h 13.367971 v 3.090428"
|
||||
style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-end:url(#marker1388)" />
|
||||
<text
|
||||
id="text1428"
|
||||
y="84.689873"
|
||||
x="81.704636"
|
||||
style="font-size:2.82223px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;stroke-width:0.264583"
|
||||
xml:space="preserve"><tspan
|
||||
style="stroke-width:0.264583"
|
||||
y="84.689873"
|
||||
x="81.704636"
|
||||
id="tspan1426"
|
||||
sodipodi:role="line">(2, 1)</tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:2.82223px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;stroke-width:0.264583"
|
||||
x="66.704636"
|
||||
y="69.723213"
|
||||
id="text1428-6"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan1426-0"
|
||||
x="66.704636"
|
||||
y="69.723213"
|
||||
style="stroke-width:0.264583">(1, 0)</tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:2.82223px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;stroke-width:0.264583"
|
||||
x="51.704636"
|
||||
y="86.927185"
|
||||
id="text1428-62"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan1426-6"
|
||||
x="51.704636"
|
||||
y="86.927185"
|
||||
style="stroke-width:0.264583">(0, 1)</tspan></text>
|
||||
<path
|
||||
id="path1646"
|
||||
d="m 85.036709,74.435098 -0.0099,1.431015"
|
||||
style="fill:none;stroke:#000000;stroke-width:0.264583;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-start:url(#Arrow2Sstart);marker-end:url(#Arrow2Send)" />
|
||||
<text
|
||||
id="text1720"
|
||||
y="70.049362"
|
||||
x="100.38699"
|
||||
style="font-size:2.82223px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;stroke-width:0.264583"
|
||||
xml:space="preserve"><tspan
|
||||
style="stroke-width:0.264583"
|
||||
y="70.049362"
|
||||
x="100.38699"
|
||||
id="tspan1718"
|
||||
sodipodi:role="line">segment offset</tspan></text>
|
||||
<path
|
||||
sodipodi:nodetypes="cc"
|
||||
id="path1722"
|
||||
d="M 100.24627,70.152448 85.644108,75.098641"
|
||||
style="fill:none;stroke:#000000;stroke-width:0.264583;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:0.264583, 0.529166;stroke-dashoffset:0;stroke-opacity:1;marker-end:url(#marker1726)" />
|
||||
<path
|
||||
style="fill:none;stroke:#000000;stroke-width:0.264583;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-start:url(#Arrow2Sstart-1);marker-end:url(#Arrow2Send-7)"
|
||||
d="m 68.570639,77.662689 2.143251,0.0099"
|
||||
id="path1646-2" />
|
||||
<path
|
||||
sodipodi:nodetypes="cc"
|
||||
id="path2057"
|
||||
d="m 71.333422,76.411802 v 1.307449"
|
||||
style="fill:none;stroke:#000000;stroke-width:0.264583;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:0.264583, 0.264583;stroke-dashoffset:0;stroke-opacity:1" />
|
||||
<path
|
||||
style="fill:none;stroke:#000000;stroke-width:0.264583;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:0.264583, 0.529167;stroke-dashoffset:0;stroke-opacity:1;marker-end:url(#marker1726-0)"
|
||||
d="M 100.24627,70.152448 72.043698,77.555282"
|
||||
id="path1722-3"
|
||||
sodipodi:nodetypes="cc" />
|
||||
<path
|
||||
sodipodi:nodetypes="cccccc"
|
||||
id="path2302"
|
||||
d="m 85.503654,88.13046 0.02905,1.836997 h 13.86685 V 76.894177 H 87.138853 v 2.494365"
|
||||
style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-end:url(#marker2306)" />
|
||||
<path
|
||||
sodipodi:nodetypes="cc"
|
||||
style="fill:none;stroke:#000000;stroke-width:0.264583;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-start:url(#Arrow2Sstart-7);marker-end:url(#Arrow2Send-9)"
|
||||
d="m 88.729129,90.472837 -0.0099,2.246409"
|
||||
id="path1646-28" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:2.82223px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;stroke-width:0.264583"
|
||||
x="101.43018"
|
||||
y="90.213501"
|
||||
id="text1720-9"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan1718-7"
|
||||
x="101.43018"
|
||||
y="90.213501"
|
||||
style="stroke-width:0.264583">negative</tspan><tspan
|
||||
id="tspan2627"
|
||||
sodipodi:role="line"
|
||||
x="101.43018"
|
||||
y="93.741287"
|
||||
style="stroke-width:0.264583">segment offset</tspan></text>
|
||||
<path
|
||||
sodipodi:nodetypes="cc"
|
||||
id="path1722-3-1"
|
||||
d="M 102.58403,91.354862 89.524132,91.884014"
|
||||
style="fill:none;stroke:#000000;stroke-width:0.264583;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:0.264583, 0.529167;stroke-dashoffset:0;stroke-opacity:1;marker-end:url(#marker1726-0-3)" />
|
||||
</g>
|
||||
<g
|
||||
transform="translate(-28.381867,-41.18436)"
|
||||
style="display:inline"
|
||||
inkscape:label="Layer 2"
|
||||
id="layer2"
|
||||
inkscape:groupmode="layer" />
|
||||
</svg>
|
After Width: | Height: | Size: 24 KiB |
2104
docs/doxygen-images/graph_grid_layout/graph_parent_placement.svg
Normal file
2104
docs/doxygen-images/graph_grid_layout/graph_parent_placement.svg
Normal file
File diff suppressed because it is too large
Load Diff
After Width: | Height: | Size: 58 KiB |
@ -562,6 +562,7 @@ HEADERS += \
|
||||
common/BugReporting.h \
|
||||
common/HighDpiPixmap.h \
|
||||
widgets/GraphLayout.h \
|
||||
widgets/GraphGridLayout.h \
|
||||
widgets/HexWidget.h \
|
||||
common/SelectionHighlight.h \
|
||||
common/Decompiler.h \
|
||||
@ -574,9 +575,11 @@ HEADERS += \
|
||||
common/IOModesController.h \
|
||||
common/SettingsUpgrade.h \
|
||||
dialogs/LayoutManager.h \
|
||||
common/CutterLayout.h
|
||||
common/CutterLayout.h \
|
||||
common/BinaryTrees.h \
|
||||
common/LinkedListPool.h
|
||||
|
||||
GRAPHVIZ_HEADERS = widgets/GraphGridLayout.h
|
||||
GRAPHVIZ_HEADERS = widgets/GraphvizLayout.h
|
||||
|
||||
FORMS += \
|
||||
dialogs/AboutDialog.ui \
|
||||
|
419
src/common/BinaryTrees.h
Normal file
419
src/common/BinaryTrees.h
Normal file
@ -0,0 +1,419 @@
|
||||
#ifndef BINARY_TREES_H
|
||||
#define BINARY_TREES_H
|
||||
|
||||
/** \file BinaryTrees.h
|
||||
* \brief Utilities to simplify creation of specialized augmented binary trees.
|
||||
*/
|
||||
|
||||
#include <vector>
|
||||
#include <cstdlib>
|
||||
#include <climits>
|
||||
#include <cstdint>
|
||||
#include <algorithm>
|
||||
|
||||
|
||||
/**
|
||||
* Not really a segment tree for storing segments as referred in academic literature. Can be considered a
|
||||
* full, almost perfect, augmented binary tree. In the context of competitive programming often called segment tree.
|
||||
*
|
||||
* Child classes are expected to implement updateFromChildren(NodeType&parent, NodeType& left, NodeType& right)
|
||||
* method which calculates inner node values from children nodes.
|
||||
*
|
||||
* \tparam NodeTypeT type of each tree element
|
||||
* \tparam FinalType final child class used for curiously recurring template pattern
|
||||
*/
|
||||
template<class NodeTypeT, class FinalType>
|
||||
class SegmentTreeBase
|
||||
{
|
||||
public:
|
||||
using NodePosition = size_t;
|
||||
using NodeType = NodeTypeT;
|
||||
|
||||
/**
|
||||
* @brief Create tree with \a size leaves.
|
||||
* @param size number of leaves in the tree
|
||||
*/
|
||||
explicit SegmentTreeBase(size_t size)
|
||||
: size(size)
|
||||
, nodeCount(2 * size)
|
||||
, nodes(nodeCount)
|
||||
{}
|
||||
|
||||
/**
|
||||
* @brief Create a tree with given size and initial value.
|
||||
*
|
||||
* Inner nodes are calculated from leaves.
|
||||
* @param size number of leaves
|
||||
* @param initialValue initial leave value
|
||||
*/
|
||||
SegmentTreeBase(size_t size, const NodeType &initialValue)
|
||||
: SegmentTreeBase(size)
|
||||
{
|
||||
init(initialValue);
|
||||
}
|
||||
protected:
|
||||
// Curiously recurring template pattern
|
||||
FinalType &This()
|
||||
{
|
||||
return static_cast<FinalType &>(*this);
|
||||
}
|
||||
|
||||
// Curiously recurring template pattern
|
||||
const FinalType &This() const
|
||||
{
|
||||
return static_cast<const FinalType &>(*this);
|
||||
}
|
||||
|
||||
size_t leavePositionToIndex(NodePosition pos) const
|
||||
{
|
||||
return pos - size;
|
||||
}
|
||||
|
||||
NodePosition leaveIndexToPosition(size_t index) const
|
||||
{
|
||||
return index + size;
|
||||
}
|
||||
|
||||
bool isLeave(NodePosition position) const
|
||||
{
|
||||
return position >= size;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Calculate inner node values from leaves.
|
||||
*/
|
||||
void buildInnerNodes()
|
||||
{
|
||||
for (size_t i = size - 1; i > 0; i--) {
|
||||
This().updateFromChildren(nodes[i], nodes[i << 1], nodes[(i << 1) | 1]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Initialize leaves with given value.
|
||||
* @param value value that will be assigned to leaves
|
||||
*/
|
||||
void init(const NodeType &value)
|
||||
{
|
||||
std::fill_n(nodes.begin() + size, size, value);
|
||||
buildInnerNodes();
|
||||
}
|
||||
|
||||
const size_t size; //< number of leaves and also index of left most leave
|
||||
const size_t nodeCount;
|
||||
std::vector<NodeType> nodes;
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Tree for point modification and range queries.
|
||||
*/
|
||||
template<class NodeType, class FinalType>
|
||||
class PointSetSegmentTree : public SegmentTreeBase<NodeType, FinalType>
|
||||
{
|
||||
using BaseType = SegmentTreeBase<NodeType, FinalType>;
|
||||
public:
|
||||
using BaseType::BaseType;
|
||||
|
||||
/**
|
||||
* @brief Set leave \a index to \a value.
|
||||
* @param index Leave index, should be in the range [0,size)
|
||||
* @param value
|
||||
*/
|
||||
void set(size_t index, const NodeType &value)
|
||||
{
|
||||
auto pos = this->leaveIndexToPosition(index);
|
||||
this->nodes[pos] = value;
|
||||
while (pos > 1) {
|
||||
auto parrent = pos >> 1;
|
||||
this->This().updateFromChildren(this->nodes[parrent], this->nodes[pos], this->nodes[pos ^ 1]);
|
||||
pos = parrent;
|
||||
}
|
||||
}
|
||||
|
||||
const NodeType &valueAtPoint(size_t index) const
|
||||
{
|
||||
return this->nodes[this->leaveIndexToPosition(index)];
|
||||
}
|
||||
|
||||
// Implement range query when necessary
|
||||
};
|
||||
|
||||
class PointSetMinTree : public PointSetSegmentTree<int, PointSetMinTree>
|
||||
{
|
||||
using BaseType = PointSetSegmentTree<int, PointSetMinTree>;
|
||||
public:
|
||||
using NodeType = int;
|
||||
|
||||
using BaseType::BaseType;
|
||||
|
||||
void updateFromChildren(NodeType &parent, NodeType &leftChild, NodeType &rightChild)
|
||||
{
|
||||
parent = std::min(leftChild, rightChild);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Find right most position with value than less than given in range [0; position].
|
||||
* @param position inclusive right side of query range
|
||||
* @param value search for position less than this
|
||||
* @return returns the position with searched property or -1 if there is no such position.
|
||||
*/
|
||||
int rightMostLessThan(size_t position, int value)
|
||||
{
|
||||
auto isGood = [&](size_t pos) {
|
||||
return nodes[pos] < value;
|
||||
};
|
||||
// right side exclusive range [l;r)
|
||||
size_t goodSubtree = 0;
|
||||
for (size_t l = leaveIndexToPosition(0), r = leaveIndexToPosition(position + 1); l < r;
|
||||
l >>= 1, r >>= 1) {
|
||||
if (l & 1) {
|
||||
if (isGood(l)) {
|
||||
// mark subtree as good but don't stop yet, there might be something good further to the right
|
||||
goodSubtree = l;
|
||||
}
|
||||
++l;
|
||||
}
|
||||
if (r & 1) {
|
||||
--r;
|
||||
if (isGood(r)) {
|
||||
goodSubtree = r;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!goodSubtree) {
|
||||
return -1;
|
||||
}
|
||||
// find rightmost good leave
|
||||
while (goodSubtree < size) {
|
||||
goodSubtree = (goodSubtree << 1) + 1;
|
||||
if (!isGood(goodSubtree)) {
|
||||
goodSubtree ^= 1;
|
||||
}
|
||||
}
|
||||
return leavePositionToIndex(goodSubtree);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Find left most position with value less than \a value in range [position; size).
|
||||
* @param position inclusive left side of query range
|
||||
* @param value search for position less than this
|
||||
* @return returns the position with searched property or -1 if there is no such position.
|
||||
*/
|
||||
int leftMostLessThan(size_t position, int value)
|
||||
{
|
||||
auto isGood = [&](size_t pos) {
|
||||
return nodes[pos] < value;
|
||||
};
|
||||
// right side exclusive range [l;r)
|
||||
size_t goodSubtree = 0;
|
||||
for (size_t l = leaveIndexToPosition(position), r = leaveIndexToPosition(size); l < r;
|
||||
l >>= 1, r >>= 1) {
|
||||
if (l & 1) {
|
||||
if (isGood(l)) {
|
||||
goodSubtree = l;
|
||||
break;
|
||||
}
|
||||
++l;
|
||||
}
|
||||
if (r & 1) {
|
||||
--r;
|
||||
if (isGood(r)) {
|
||||
goodSubtree = r;
|
||||
// mark subtree as good but don't stop yet, there might be something good further to the left
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!goodSubtree) {
|
||||
return -1;
|
||||
}
|
||||
// find leftmost good leave
|
||||
while (goodSubtree < size) {
|
||||
goodSubtree = (goodSubtree << 1);
|
||||
if (!isGood(goodSubtree)) {
|
||||
goodSubtree ^= 1;
|
||||
}
|
||||
}
|
||||
return leavePositionToIndex(goodSubtree);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Tree that supports lazily applying an operation to range.
|
||||
*
|
||||
* Each inner node has a promise value describing an operation that needs to be applied to corresponding subtree.
|
||||
*
|
||||
* Child classes are expected to implement to pushDown(size_t nodePosition) method. Which applies the applies the
|
||||
* operation stored in \a promise for nodePosition to the direct children nodes.
|
||||
*
|
||||
* \tparam NodeType type of tree nodes
|
||||
* \tparam PromiseType type describing operation that needs to be applied to subtree
|
||||
* \tparam FinalType child class type for CRTP. See SegmentTreeBase
|
||||
*/
|
||||
template <class NodeType, class PromiseType, class FinalType>
|
||||
class LazySegmentTreeBase : public SegmentTreeBase<NodeType, FinalType>
|
||||
{
|
||||
using BaseType = SegmentTreeBase<NodeType, FinalType>;
|
||||
public:
|
||||
/**
|
||||
* @param size Number of tree leaves.
|
||||
* @param neutralPromise Promise value that doesn't modify tree nodes.
|
||||
*/
|
||||
LazySegmentTreeBase(size_t size, const PromiseType &neutralPromise)
|
||||
: BaseType(size)
|
||||
, neutralPromiseElement(neutralPromise)
|
||||
, promise(size, neutralPromise)
|
||||
{
|
||||
h = 0;
|
||||
size_t v = size;
|
||||
while (v) {
|
||||
v >>= 1;
|
||||
h++;
|
||||
}
|
||||
}
|
||||
|
||||
LazySegmentTreeBase(size_t size, NodeType value, PromiseType neutralPromise)
|
||||
: LazySegmentTreeBase(size, neutralPromise)
|
||||
{
|
||||
this->init(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Calculate the tree operation over the range [\a l, \a r)
|
||||
* @param l inclusive range left side
|
||||
* @param r exclusive range right side
|
||||
* @param initialValue Initial value for aggregate operation.
|
||||
* @return Tree operation calculated over the range.
|
||||
*/
|
||||
NodeType rangeOperation(size_t l, size_t r, NodeType initialValue)
|
||||
{
|
||||
NodeType result = initialValue;
|
||||
l = this->leaveIndexToPosition(l);
|
||||
r = this->leaveIndexToPosition(r);
|
||||
pushDownFromRoot(l);
|
||||
pushDownFromRoot(r - 1);
|
||||
for (; l < r; l >>= 1, r >>= 1) {
|
||||
if (l & 1) {
|
||||
This().updateFromChildren(result, result, this->nodes[l++]);
|
||||
}
|
||||
if (r & 1) {
|
||||
This().updateFromChildren(result, result, this->nodes[--r]);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief Ensure that all the parents of node \a p have the operation applied.
|
||||
* @param p Node position
|
||||
*/
|
||||
void pushDownFromRoot(typename BaseType::NodePosition p)
|
||||
{
|
||||
for (size_t i = h; i > 0; i--) {
|
||||
This().pushDown(p >> i);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Update all the inner nodes in path from \a p to root.
|
||||
* @param p node position
|
||||
*/
|
||||
void updateUntilRoot(typename BaseType::NodePosition p)
|
||||
{
|
||||
while (p > 1) {
|
||||
auto parent = p >> 1;
|
||||
if (promise[parent] == neutralPromiseElement) {
|
||||
This().updateFromChildren(this->nodes[parent], this->nodes[p & ~size_t(1)], this->nodes[p | 1]);
|
||||
}
|
||||
p = parent;
|
||||
}
|
||||
}
|
||||
|
||||
using BaseType::This;
|
||||
|
||||
int h; //< Tree height
|
||||
const PromiseType neutralPromiseElement;
|
||||
std::vector<PromiseType> promise;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Structure supporting range assignment and range maximum operations.
|
||||
*/
|
||||
class RangeAssignMaxTree : public LazySegmentTreeBase<int, uint8_t, RangeAssignMaxTree>
|
||||
{
|
||||
using BaseType = LazySegmentTreeBase<int, uint8_t, RangeAssignMaxTree>;
|
||||
public:
|
||||
using ValueType = int;
|
||||
RangeAssignMaxTree(size_t size, ValueType initialValue)
|
||||
: BaseType(size, initialValue, 0)
|
||||
{
|
||||
}
|
||||
|
||||
void updateFromChildren(NodeType &parent, const NodeType &left, const NodeType &right)
|
||||
{
|
||||
parent = std::max(left, right);
|
||||
}
|
||||
|
||||
void pushDown(size_t parent)
|
||||
{
|
||||
if (promise[parent]) {
|
||||
size_t left = (parent << 1);
|
||||
size_t right = (parent << 1) | 1;
|
||||
nodes[left] = nodes[right] = nodes[parent];
|
||||
if (left < size) {
|
||||
promise[left] = promise[parent];
|
||||
}
|
||||
if (right < size) {
|
||||
promise[right] = promise[parent];
|
||||
}
|
||||
promise[parent] = neutralPromiseElement;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Change all the elements in range [\a left, \a right) to \a value.
|
||||
* @param left inclusive range left side
|
||||
* @param right exclusive right side of range
|
||||
* @param value value to be assigned
|
||||
*/
|
||||
void setRange(size_t left, size_t right, NodeType value)
|
||||
{
|
||||
left = leaveIndexToPosition(left);
|
||||
right = leaveIndexToPosition(right);
|
||||
pushDownFromRoot(left);
|
||||
pushDownFromRoot(right - 1);
|
||||
for (size_t l = left, r = right; l < r; l >>= 1, r >>= 1) {
|
||||
if (l & 1) {
|
||||
nodes[l] = value;
|
||||
if (!isLeave(l)) {
|
||||
promise[l] = 1;
|
||||
}
|
||||
l += 1;
|
||||
}
|
||||
if (r & 1) {
|
||||
r -= 1;
|
||||
nodes[r] = value;
|
||||
if (!isLeave(r)) {
|
||||
promise[r] = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
updateUntilRoot(left);
|
||||
updateUntilRoot(right - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Calculate biggest value in the range [l, r)
|
||||
* @param l inclusive left side of range
|
||||
* @param r exclusive right side of range
|
||||
* @return biggest value in given range
|
||||
*/
|
||||
int rangeMaximum(size_t l, size_t r)
|
||||
{
|
||||
return rangeOperation(l, r, std::numeric_limits<ValueType>::min());
|
||||
}
|
||||
};
|
||||
|
||||
#endif // BINARY_TREES_H
|
170
src/common/LinkedListPool.h
Normal file
170
src/common/LinkedListPool.h
Normal file
@ -0,0 +1,170 @@
|
||||
#ifndef LINKED_LIST_POOL_H
|
||||
#define LINKED_LIST_POOL_H
|
||||
|
||||
#include <vector>
|
||||
#include <cstdint>
|
||||
#include <iterator>
|
||||
|
||||
/**
|
||||
* @brief Pool of singly linked lists.
|
||||
*
|
||||
* Should not be used as general purpose container. Use only for algorithms that require linked lists ability
|
||||
* to split and concatenate them. All the data is owned by LinkedListPool.
|
||||
*
|
||||
* In contrast to std::list and std::forward_list doesn't allocate each node separately. LinkedListPool can reserve
|
||||
* all the memory for multiple lists during construction. Uses std::vector as backing container.
|
||||
*/
|
||||
template<class T>
|
||||
class LinkedListPool
|
||||
{
|
||||
using IndexType = size_t;
|
||||
struct Item {
|
||||
IndexType next;
|
||||
T value;
|
||||
};
|
||||
public:
|
||||
/**
|
||||
* @brief Single list within LinkedListPool.
|
||||
*
|
||||
* List only refers to chain of elements. Copying it doesn't copy any element. Item data is owned by
|
||||
* LinkedListPool.
|
||||
*
|
||||
* Use LinkedListPool::makeList to create non-empty list.
|
||||
*/
|
||||
class List
|
||||
{
|
||||
IndexType head = 0;
|
||||
IndexType tail = 0;
|
||||
friend class LinkedListPool;
|
||||
List(IndexType head, IndexType tail)
|
||||
: head(head)
|
||||
, tail(tail)
|
||||
{}
|
||||
public:
|
||||
/**
|
||||
* @brief Create an empty list
|
||||
*/
|
||||
List() = default;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief List iterator.
|
||||
*
|
||||
* Iterators don't get invalidated by adding items to list, but the items may be relocated.
|
||||
*/
|
||||
class ListIterator
|
||||
{
|
||||
IndexType index = 0;
|
||||
LinkedListPool<T> *pool = nullptr;
|
||||
ListIterator(IndexType index, LinkedListPool<T> *pool)
|
||||
: index(index)
|
||||
, pool(pool)
|
||||
{}
|
||||
|
||||
friend class LinkedListPool<T>;
|
||||
public:
|
||||
using iterator_category = std::forward_iterator_tag;
|
||||
using value_type = T;
|
||||
using difference_type = size_t;
|
||||
using pointer = T*;
|
||||
using reference = T&;
|
||||
ListIterator() = default;
|
||||
reference operator*()
|
||||
{
|
||||
return pool->data[index].value;
|
||||
}
|
||||
ListIterator &operator++()
|
||||
{
|
||||
index = pool->data[index].next;
|
||||
return *this;
|
||||
}
|
||||
ListIterator operator++(int)
|
||||
{
|
||||
ListIterator tmp(*this);
|
||||
operator++();
|
||||
return tmp;
|
||||
}
|
||||
bool operator!=(const ListIterator &b) const
|
||||
{
|
||||
return index != b.index || pool != b.pool;
|
||||
};
|
||||
/**
|
||||
* @brief Test if iterator points to valid value.
|
||||
*/
|
||||
operator bool() const
|
||||
{
|
||||
return index;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Create a linked list pool with capacity for \a initialCapacity list items.
|
||||
* @param initialCapacity number of elements to preallocate.
|
||||
*/
|
||||
LinkedListPool(size_t initialCapacity)
|
||||
: data(1)
|
||||
{
|
||||
data.reserve(initialCapacity + 1); // [0] element reserved
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Create a list containing single item.
|
||||
*
|
||||
* Does not invalidate any iterators, but may cause item relocation when initialCapacity is exceeded.
|
||||
* @param value value of element that will be inserted in the created list
|
||||
* @return List containing single value \a value .
|
||||
*/
|
||||
List makeList(const T &value)
|
||||
{
|
||||
size_t position = data.size();
|
||||
data.push_back(Item{0, value});
|
||||
return {position, position};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Split list and return second half.
|
||||
*
|
||||
* After performing the operation, list passed as argument and return list point to the same items. Modifying them
|
||||
* will affect both lists.
|
||||
*
|
||||
* @param list The list that needs to be split.
|
||||
* @param head Iterator to the first item in new list. Needs to be within \a list .
|
||||
* @return Returns suffix of \a list.
|
||||
*/
|
||||
List splitTail(const List &list, const ListIterator &head)
|
||||
{
|
||||
return List {head.index, list.tail};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Create list iterator from list.
|
||||
* @param list
|
||||
* @return Iterator pointing to the first item in the list.
|
||||
*/
|
||||
ListIterator head(const List &list)
|
||||
{
|
||||
return iteratorFromIndex(list.head);
|
||||
}
|
||||
|
||||
ListIterator end(const List &list)
|
||||
{
|
||||
return std::next(iteratorFromIndex(list.tail));
|
||||
}
|
||||
|
||||
List append(const List &head, const List &tail)
|
||||
{
|
||||
List result{head.head, tail.tail};
|
||||
data[head.tail].next = tail.head;
|
||||
return result;
|
||||
}
|
||||
private:
|
||||
ListIterator iteratorFromIndex(IndexType index)
|
||||
{
|
||||
return ListIterator{ index, this };
|
||||
}
|
||||
|
||||
std::vector<Item> data;
|
||||
};
|
||||
|
||||
|
||||
#endif // LINKED_LIST_POOL
|
@ -134,7 +134,7 @@ private:
|
||||
};
|
||||
|
||||
/**
|
||||
* @class This class is used to draw the left pane of the disassembly
|
||||
* This class is used to draw the left pane of the disassembly
|
||||
* widget. Its goal is to draw proper arrows for the jumps of the disassembly.
|
||||
*/
|
||||
class DisassemblyLeftPanel: public QFrame
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -3,7 +3,12 @@
|
||||
|
||||
#include "core/Cutter.h"
|
||||
#include "GraphLayout.h"
|
||||
#include "common/LinkedListPool.h"
|
||||
|
||||
|
||||
/**
|
||||
* @brief Graph layout algorithm on layered graph layout approach. For simplicity all the nodes are placed in a grid.
|
||||
*/
|
||||
class GraphGridLayout : public GraphLayout
|
||||
{
|
||||
public:
|
||||
@ -20,42 +25,53 @@ public:
|
||||
int &height) const override;
|
||||
private:
|
||||
LayoutType layoutType;
|
||||
/// false - use bounding box for smallest subtree when placing them side by side
|
||||
bool tightSubtreePlacement = false;
|
||||
/// true if code should try to place parent between direct children as much as possible
|
||||
bool parentBetweenDirectChild = false;
|
||||
/// false if blocks in rows should be aligned at top, true for middle alignment
|
||||
bool verticalBlockAlignmentMiddle = false;
|
||||
|
||||
struct GridBlock {
|
||||
ut64 id;
|
||||
std::vector<ut64> tree_edge; // subset of outgoing edges that form a tree
|
||||
std::vector<ut64> dag_edge; // subset of outgoing edges that form a tree
|
||||
std::vector<ut64> tree_edge; //!< subset of outgoing edges that form a tree
|
||||
std::vector<ut64> dag_edge; //!< subset of outgoing edges that form a dag
|
||||
std::size_t has_parent = false;
|
||||
int level = 0;
|
||||
int inputCount = 0;
|
||||
int outputCount = 0;
|
||||
|
||||
// Number of rows in block
|
||||
/// Number of rows in subtree
|
||||
int row_count = 0;
|
||||
// Number of columns in block
|
||||
int col_count = 0;
|
||||
// Column in which the block is
|
||||
/// Column in which the block is
|
||||
int col = 0;
|
||||
// Row in which the block is
|
||||
/// Row in which the block is
|
||||
int row = 0;
|
||||
|
||||
int lastRowLeft; //!< left side of subtree last row
|
||||
int lastRowRight; //!< right side of subtree last row
|
||||
int leftPosition; //!< left side of subtree
|
||||
int rightPosition; //!< right side of subtree
|
||||
LinkedListPool<int>::List leftSideShape;
|
||||
LinkedListPool<int>::List rightSideShape;
|
||||
};
|
||||
|
||||
struct Point {
|
||||
int row; //point[0]
|
||||
int col; //point[1]
|
||||
int index; //point[2]
|
||||
int row;
|
||||
int col;
|
||||
int offset;
|
||||
int16_t kind;
|
||||
int16_t spacingOverride;
|
||||
};
|
||||
|
||||
struct GridEdge {
|
||||
ut64 dest;
|
||||
int mainColumn = -1;
|
||||
std::vector<Point> points;
|
||||
int start_index = 0;
|
||||
QPolygonF polyline;
|
||||
int secondaryPriority;
|
||||
|
||||
void addPoint(int row, int col, int index = 0)
|
||||
void addPoint(int row, int col, int16_t kind = 0)
|
||||
{
|
||||
Point point = {row, col, 0};
|
||||
this->points.push_back(point);
|
||||
if (int(this->points.size()) > 1)
|
||||
this->points[this->points.size() - 2].index = index;
|
||||
this->points.push_back({row, col, 0, kind, 0});
|
||||
}
|
||||
};
|
||||
|
||||
@ -63,29 +79,92 @@ private:
|
||||
std::unordered_map<ut64, GridBlock> grid_blocks;
|
||||
std::unordered_map<ut64, GraphBlock> *blocks = nullptr;
|
||||
std::unordered_map<ut64, std::vector<GridEdge>> edge;
|
||||
size_t rows = -1;
|
||||
size_t columns = -1;
|
||||
std::vector<int> columnWidth;
|
||||
std::vector<int> rowHeight;
|
||||
std::vector<int> edgeColumnWidth;
|
||||
std::vector<int> edgeRowHeight;
|
||||
|
||||
std::vector<int> columnOffset;
|
||||
std::vector<int> rowOffset;
|
||||
std::vector<int> edgeColumnOffset;
|
||||
std::vector<int> edgeRowOffset;
|
||||
};
|
||||
|
||||
using GridBlockMap = std::unordered_map<ut64, GridBlock>;
|
||||
|
||||
/**
|
||||
* @brief Find nodes where control flow merges after splitting.
|
||||
* Sets node column offset so that after computing placement merge point is centered bellow nodes above.
|
||||
*/
|
||||
void findMergePoints(LayoutState &state) const;
|
||||
/**
|
||||
* @brief Compute node rows and columns within grid.
|
||||
* @param blockOrder Nodes in the reverse topological order.
|
||||
*/
|
||||
void computeAllBlockPlacement(const std::vector<ut64> &blockOrder,
|
||||
LayoutState &layoutState) const;
|
||||
void computeBlockPlacement(ut64 blockId,
|
||||
LayoutState &layoutState) const;
|
||||
void adjustGraphLayout(GridBlock &block, GridBlockMap &blocks,
|
||||
int col, int row) const;
|
||||
/**
|
||||
* @brief Perform the topological sorting of graph nodes.
|
||||
* If the graph contains loops, a subset of edges is selected. Subset of edges forming DAG are stored in
|
||||
* GridBlock::dag_edge.
|
||||
* @param state Graph layout state including the input graph.
|
||||
* @param entry Entrypoint node. When removing loops prefer placing this node at top.
|
||||
* @return Reverse topological ordering.
|
||||
*/
|
||||
static std::vector<ut64> topoSort(LayoutState &state, ut64 entry);
|
||||
|
||||
// Edge computing stuff
|
||||
template<typename T>
|
||||
using Matrix = std::vector<std::vector<T>>;
|
||||
using EdgesVector = Matrix<std::vector<bool>>;
|
||||
/**
|
||||
* @brief Assign row positions to nodes.
|
||||
* @param state
|
||||
* @param blockOrder reverse topological ordering of nodes
|
||||
*/
|
||||
static void assignRows(LayoutState &state, const std::vector<ut64> &blockOrder);
|
||||
/**
|
||||
* @brief Select subset of DAG edges that form tree.
|
||||
* @param state
|
||||
*/
|
||||
static void selectTree(LayoutState &state);
|
||||
|
||||
GridEdge routeEdge(EdgesVector &horiz_edges, EdgesVector &vert_edges,
|
||||
Matrix<bool> &edge_valid, GridBlock &start, GridBlock &end) const;
|
||||
static int findVertEdgeIndex(EdgesVector &edges, int col, int min_row, int max_row);
|
||||
static bool isEdgeMarked(EdgesVector &edges, int row, int col, int index);
|
||||
static void markEdge(EdgesVector &edges, int row, int col, int index, bool used = true);
|
||||
static int findHorizEdgeIndex(EdgesVector &edges, int row, int min_col, int max_col);
|
||||
/**
|
||||
* @brief routeEdges Route edges, expects node positions to be calculated previously.
|
||||
*/
|
||||
void routeEdges(LayoutState &state) const;
|
||||
/**
|
||||
* @brief Choose which column to use for transition from start node row to target node row.
|
||||
*/
|
||||
void calculateEdgeMainColumn(LayoutState &state) const;
|
||||
/**
|
||||
* @brief Do rough edge routing within grid using up to 5 segments.
|
||||
*/
|
||||
void roughRouting(LayoutState &state) const;
|
||||
/**
|
||||
* @brief Calculate segment placement relative to their columns.
|
||||
*/
|
||||
void elaborateEdgePlacement(LayoutState &state) const;
|
||||
/**
|
||||
* @brief Recalculate column widths, trying to compensate for the space taken by edge columns.
|
||||
*/
|
||||
void adjustColumnWidths(LayoutState &state) const;
|
||||
/**
|
||||
* @brief Calculate position of each column(or row) based on widths.
|
||||
* It is assumed that columnWidth.size() + 1 = edgeColumnWidth.size() and they are interleaved.
|
||||
* @param columnWidth
|
||||
* @param edgeColumnWidth
|
||||
* @param columnOffset
|
||||
* @param edgeColumnOffset
|
||||
* @return total width of all the columns
|
||||
*/
|
||||
static int calculateColumnOffsets(const std::vector<int> &columnWidth, std::vector<int> &edgeColumnWidth,
|
||||
std::vector<int> &columnOffset, std::vector<int> &edgeColumnOffset);
|
||||
/**
|
||||
* @brief Final graph layout step. Convert grids cell relative positions to absolute pixel positions.
|
||||
* @param state
|
||||
* @param width image width output argument
|
||||
* @param height image height output argument
|
||||
*/
|
||||
void convertToPixelCoordinates(LayoutState &state, int &width, int &height) const;
|
||||
};
|
||||
|
||||
#endif // GRAPHGRIDLAYOUT_H
|
||||
|
@ -32,8 +32,10 @@ public:
|
||||
using Graph = std::unordered_map<ut64, GraphBlock>;
|
||||
|
||||
struct LayoutConfig {
|
||||
int block_vertical_margin = 40;
|
||||
int block_horizontal_margin = 10;
|
||||
int blockVerticalSpacing = 40;
|
||||
int blockHorizontalSpacing = 10;
|
||||
int edgeVerticalSpacing = 10;
|
||||
int edgeHorizontalSpacing = 10;
|
||||
};
|
||||
|
||||
GraphLayout(const LayoutConfig &layout_config) : layoutConfig(layout_config) {}
|
||||
|
Loading…
Reference in New Issue
Block a user