Revert internals of the last layering-related commit, and go back a slightly-cleaned-up version of how it was before. Remove all layering modes; only option now is add-is-higher. Move-add-higher could easily be re-added if anyone uses it.

git-svn-id: svn://localhost/ardour2/branches/3.0@11111 d708f5d6-7413-0410-9779-e7cbd77b26cf
This commit is contained in:
Carl Hetherington 2011-12-29 22:14:15 +00:00
parent cabb76cce6
commit b177514930
36 changed files with 1001 additions and 2413 deletions

View File

@ -1,6 +1,7 @@
#!/bin/sh
for f in basic-layering explicit-layering1 explicit-layering2 tricky-explicit-layering; do
for f in basic-layering layering-order-1 layering-order-2; do
echo "$f"
inkscape -z --export-area-drawing -f $f.svg --export-pdf $f.pdf
done

View File

@ -1,322 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<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="744.09448819"
height="1052.3622047"
id="svg2"
version="1.1"
inkscape:version="0.47 r22583"
sodipodi:docname="explicit-layering.svg">
<defs
id="defs4">
<marker
inkscape:stockid="Arrow2Lend"
orient="auto"
refY="0.0"
refX="0.0"
id="Arrow2Lend"
style="overflow:visible;">
<path
id="path3618"
style="font-size:12.0;fill-rule:evenodd;stroke-width:0.62500000;stroke-linejoin:round;"
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
transform="scale(1.1) rotate(180) translate(1,0)" />
</marker>
<inkscape:perspective
sodipodi:type="inkscape:persp3d"
inkscape:vp_x="0 : 526.18109 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_z="744.09448 : 526.18109 : 1"
inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
id="perspective10" />
<inkscape:perspective
id="perspective4058"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective4089"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective4120"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective4151"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective4365"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective4386"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<marker
inkscape:stockid="Arrow2Lend"
orient="auto"
refY="0"
refX="0"
id="Arrow2Lend-6"
style="overflow:visible">
<path
id="path3618-4"
style="font-size:12px;fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
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="matrix(-1.1,0,0,-1.1,-1.1,0)" />
</marker>
<inkscape:perspective
id="perspective4449"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<marker
inkscape:stockid="Arrow2Lend"
orient="auto"
refY="0"
refX="0"
id="Arrow2Lend-5"
style="overflow:visible">
<path
id="path3618-1"
style="font-size:12px;fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
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="matrix(-1.1,0,0,-1.1,-1.1,0)" />
</marker>
<inkscape:perspective
id="perspective4668"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective4696"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective4696-1"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="2.633643"
inkscape:cx="191.36241"
inkscape:cy="670.78783"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="true"
inkscape:window-width="1280"
inkscape:window-height="949"
inkscape:window-x="0"
inkscape:window-y="25"
inkscape:window-maximized="1">
<inkscape:grid
type="xygrid"
id="grid2816"
empspacing="5"
visible="true"
enabled="true"
snapvisiblegridlinesonly="true"
units="mm"
spacingx="5mm"
spacingy="5mm" />
</sodipodi:namedview>
<metadata
id="metadata7">
<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
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<path
style="fill:none;stroke:#000000;stroke-width:0.62500000000000000;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-end:url(#Arrow2Lend);color:#000000;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
d="m 88.582677,432.28344 159.448823,0"
id="path3592" />
<text
xml:space="preserve"
style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#000000;fill-opacity:1;stroke:none;font-family:LMRoman12;-inkscape-font-specification:LMRoman12"
x="255.20872"
y="435.23877"
id="text4042"><tspan
sodipodi:role="line"
id="tspan4044"
x="255.20872"
y="435.23877">time</tspan></text>
<rect
style="color:#000000;fill:none;stroke:#000000;stroke-width:0.625;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
id="rect2818"
width="106.29921"
height="35.433071"
x="88.582687"
y="308.2677" />
<rect
style="color:#000000;fill:none;stroke:#000000;stroke-width:0.625;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
id="rect2818-2"
width="88.582687"
height="35.433071"
x="124.01574"
y="343.70078" />
<rect
style="color:#000000;fill:none;stroke:#000000;stroke-width:0.625;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
id="rect2818-2-2"
width="70.86615"
height="35.433071"
x="177.16534"
y="379.13385" />
<rect
style="color:#000000;fill:none;stroke:#000000;stroke-width:0.625;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
id="rect2818-2-2-4"
width="35.433086"
height="35.433071"
x="265.74802"
y="379.13385" />
<text
xml:space="preserve"
style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#000000;fill-opacity:1;stroke:none;font-family:LMRoman12;-inkscape-font-specification:LMRoman12"
x="137.33429"
y="330.26825"
id="text4320"><tspan
sodipodi:role="line"
id="tspan4322"
x="137.33429"
y="330.26825">A</tspan></text>
<text
xml:space="preserve"
style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#000000;fill-opacity:1;stroke:none;font-family:LMRoman12;-inkscape-font-specification:LMRoman12"
x="164.22108"
y="365.51532"
id="text4324"><tspan
sodipodi:role="line"
id="tspan4326"
x="164.22108"
y="365.51532">B</tspan></text>
<text
xml:space="preserve"
style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#000000;fill-opacity:1;stroke:none;font-family:LMRoman12;-inkscape-font-specification:LMRoman12"
x="208.36243"
y="400.94839"
id="text4328"><tspan
sodipodi:role="line"
id="tspan4330"
x="208.36243"
y="400.94839">C</tspan></text>
<text
xml:space="preserve"
style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#000000;fill-opacity:1;stroke:none;font-family:LMRoman12;-inkscape-font-specification:LMRoman12"
x="279.05457"
y="400.94839"
id="text4332"><tspan
sodipodi:role="line"
id="tspan4334"
x="279.05457"
y="400.94839">D</tspan></text>
<path
style="color:#000000;fill:none;stroke:#000000;stroke-width:0.625;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;marker-end:url(#Arrow2Lend);visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
d="m 318.89764,432.28344 0,-124.01575"
id="path3592-8"
sodipodi:nodetypes="cc" />
<text
xml:space="preserve"
style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#000000;fill-opacity:1;stroke:none;font-family:LMRoman12;-inkscape-font-specification:LMRoman12"
x="307.30875"
y="301.75519"
id="text4406"><tspan
sodipodi:role="line"
id="tspan4408"
x="307.30875"
y="301.75519">layer</tspan></text>
<path
style="fill:none;stroke:#000000;stroke-width:0.62500000000000000;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;color:#000000;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;marker:none;marker-end:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
d="m 311.84462,397.273 13.54507,0"
id="path4439" />
<text
xml:space="preserve"
style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#000000;fill-opacity:1;stroke:none;font-family:LMRoman12;-inkscape-font-specification:LMRoman12"
x="328.09393"
y="401.58279"
id="text4656"><tspan
sodipodi:role="line"
id="tspan4658"
x="328.09393"
y="401.58279">0</tspan></text>
<path
style="color:#000000;fill:none;stroke:#000000;stroke-width:0.625;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
d="m 312.32197,361.87109 13.54507,0"
id="path4439-1" />
<text
xml:space="preserve"
style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#000000;fill-opacity:1;stroke:none;font-family:LMRoman12;-inkscape-font-specification:LMRoman12"
x="328.02194"
y="366.18088"
id="text4656-0-0"><tspan
sodipodi:role="line"
id="tspan4658-6-3"
x="328.02194"
y="366.18088">1</tspan></text>
<path
style="color:#000000;fill:none;stroke:#000000;stroke-width:0.625;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
d="m 311.84462,325.98423 13.54507,0"
id="path4439-1-6" />
<text
xml:space="preserve"
style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#000000;fill-opacity:1;stroke:none;font-family:LMRoman12;-inkscape-font-specification:LMRoman12"
x="328.09393"
y="329.49646"
id="text4742"><tspan
sodipodi:role="line"
id="tspan4744"
x="328.09393"
y="329.49646">2</tspan></text>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 13 KiB

View File

@ -1,322 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<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="744.09448819"
height="1052.3622047"
id="svg2"
version="1.1"
inkscape:version="0.47 r22583"
sodipodi:docname="explicit-layering2.svg">
<defs
id="defs4">
<marker
inkscape:stockid="Arrow2Lend"
orient="auto"
refY="0.0"
refX="0.0"
id="Arrow2Lend"
style="overflow:visible;">
<path
id="path3618"
style="font-size:12.0;fill-rule:evenodd;stroke-width:0.62500000;stroke-linejoin:round;"
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
transform="scale(1.1) rotate(180) translate(1,0)" />
</marker>
<inkscape:perspective
sodipodi:type="inkscape:persp3d"
inkscape:vp_x="0 : 526.18109 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_z="744.09448 : 526.18109 : 1"
inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
id="perspective10" />
<inkscape:perspective
id="perspective4058"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective4089"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective4120"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective4151"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective4365"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective4386"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<marker
inkscape:stockid="Arrow2Lend"
orient="auto"
refY="0"
refX="0"
id="Arrow2Lend-6"
style="overflow:visible">
<path
id="path3618-4"
style="font-size:12px;fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
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="matrix(-1.1,0,0,-1.1,-1.1,0)" />
</marker>
<inkscape:perspective
id="perspective4449"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<marker
inkscape:stockid="Arrow2Lend"
orient="auto"
refY="0"
refX="0"
id="Arrow2Lend-5"
style="overflow:visible">
<path
id="path3618-1"
style="font-size:12px;fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
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="matrix(-1.1,0,0,-1.1,-1.1,0)" />
</marker>
<inkscape:perspective
id="perspective4668"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective4696"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective4696-1"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="2.633643"
inkscape:cx="191.36241"
inkscape:cy="670.02843"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="true"
inkscape:window-width="1280"
inkscape:window-height="949"
inkscape:window-x="0"
inkscape:window-y="25"
inkscape:window-maximized="1">
<inkscape:grid
type="xygrid"
id="grid2816"
empspacing="5"
visible="true"
enabled="true"
snapvisiblegridlinesonly="true"
units="mm"
spacingx="5mm"
spacingy="5mm" />
</sodipodi:namedview>
<metadata
id="metadata7">
<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 />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<path
style="fill:none;stroke:#000000;stroke-width:0.62500000000000000;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-end:url(#Arrow2Lend);color:#000000;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
d="m 88.582677,432.28344 159.448823,0"
id="path3592" />
<text
xml:space="preserve"
style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#000000;fill-opacity:1;stroke:none;font-family:LMRoman12;-inkscape-font-specification:LMRoman12"
x="255.20872"
y="435.23877"
id="text4042"><tspan
sodipodi:role="line"
id="tspan4044"
x="255.20872"
y="435.23877">time</tspan></text>
<rect
style="color:#000000;fill:none;stroke:#000000;stroke-width:0.625;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
id="rect2818"
width="106.29921"
height="35.433071"
x="88.58268"
y="343.70078" />
<rect
style="color:#000000;fill:none;stroke:#000000;stroke-width:0.625;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
id="rect2818-2"
width="88.582687"
height="35.433071"
x="124.01575"
y="379.13385" />
<rect
style="color:#000000;fill:none;stroke:#000000;stroke-width:0.625;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
id="rect2818-2-2"
width="70.86615"
height="35.433071"
x="177.16534"
y="308.2677" />
<rect
style="color:#000000;fill:none;stroke:#000000;stroke-width:0.625;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
id="rect2818-2-2-4"
width="35.433086"
height="35.433071"
x="265.74802"
y="379.13385" />
<text
xml:space="preserve"
style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#000000;fill-opacity:1;stroke:none;font-family:LMRoman12;-inkscape-font-specification:LMRoman12"
x="137.33429"
y="365.70132"
id="text4320"><tspan
sodipodi:role="line"
id="tspan4322"
x="137.33429"
y="365.70132">A</tspan></text>
<text
xml:space="preserve"
style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#000000;fill-opacity:1;stroke:none;font-family:LMRoman12;-inkscape-font-specification:LMRoman12"
x="164.22108"
y="400.94839"
id="text4324"><tspan
sodipodi:role="line"
id="tspan4326"
x="164.22108"
y="400.94839">B</tspan></text>
<text
xml:space="preserve"
style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#000000;fill-opacity:1;stroke:none;font-family:LMRoman12;-inkscape-font-specification:LMRoman12"
x="208.36243"
y="330.08224"
id="text4328"><tspan
sodipodi:role="line"
id="tspan4330"
x="208.36243"
y="330.08224">C</tspan></text>
<text
xml:space="preserve"
style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#000000;fill-opacity:1;stroke:none;font-family:LMRoman12;-inkscape-font-specification:LMRoman12"
x="279.05457"
y="400.94839"
id="text4332"><tspan
sodipodi:role="line"
id="tspan4334"
x="279.05457"
y="400.94839">D</tspan></text>
<path
style="color:#000000;fill:none;stroke:#000000;stroke-width:0.625;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;marker-end:url(#Arrow2Lend);visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
d="m 318.89764,432.28344 0,-124.01575"
id="path3592-8"
sodipodi:nodetypes="cc" />
<text
xml:space="preserve"
style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#000000;fill-opacity:1;stroke:none;font-family:LMRoman12;-inkscape-font-specification:LMRoman12"
x="307.30875"
y="301.75519"
id="text4406"><tspan
sodipodi:role="line"
id="tspan4408"
x="307.30875"
y="301.75519">layer</tspan></text>
<path
style="fill:none;stroke:#000000;stroke-width:0.62500000000000000;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;color:#000000;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;marker:none;marker-end:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
d="m 311.84462,397.273 13.54507,0"
id="path4439" />
<text
xml:space="preserve"
style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#000000;fill-opacity:1;stroke:none;font-family:LMRoman12;-inkscape-font-specification:LMRoman12"
x="328.09393"
y="401.58279"
id="text4656"><tspan
sodipodi:role="line"
id="tspan4658"
x="328.09393"
y="401.58279">0</tspan></text>
<path
style="color:#000000;fill:none;stroke:#000000;stroke-width:0.625;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
d="m 312.32197,361.87109 13.54507,0"
id="path4439-1" />
<text
xml:space="preserve"
style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#000000;fill-opacity:1;stroke:none;font-family:LMRoman12;-inkscape-font-specification:LMRoman12"
x="328.02194"
y="366.18088"
id="text4656-0-0"><tspan
sodipodi:role="line"
id="tspan4658-6-3"
x="328.02194"
y="366.18088">1</tspan></text>
<path
style="color:#000000;fill:none;stroke:#000000;stroke-width:0.625;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
d="m 311.84462,325.98423 13.54507,0"
id="path4439-1-6" />
<text
xml:space="preserve"
style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#000000;fill-opacity:1;stroke:none;font-family:LMRoman12;-inkscape-font-specification:LMRoman12"
x="328.09393"
y="329.49646"
id="text4742"><tspan
sodipodi:role="line"
id="tspan4744"
x="328.09393"
y="329.49646">2</tspan></text>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 13 KiB

View File

@ -0,0 +1,201 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<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="744.09448819"
height="1052.3622047"
id="svg2"
version="1.1"
inkscape:version="0.48.2 r9819"
sodipodi:docname="New document 1">
<defs
id="defs4">
<marker
inkscape:stockid="Arrow2Mend"
orient="auto"
refY="0.0"
refX="0.0"
id="Arrow2Mend"
style="overflow:visible;">
<path
id="path3913"
style="font-size:12.0;fill-rule:evenodd;stroke-width:0.62500000;stroke-linejoin:round;"
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
transform="scale(0.6) rotate(180) translate(0,0)" />
</marker>
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="2.4179431"
inkscape:cx="170.5"
inkscape:cy="941"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:window-width="1280"
inkscape:window-height="949"
inkscape:window-x="0"
inkscape:window-y="25"
inkscape:window-maximized="1" />
<metadata
id="metadata7">
<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
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<rect
style="color:#000000;fill:none;stroke:#00ff00;stroke-width:0.625;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
id="rect2985"
width="72.585144"
height="29.6516"
x="78.1614"
y="21.362185" />
<rect
style="color:#000000;fill:none;stroke:#ff0003;stroke-width:0.625;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
id="rect2985-7"
width="72.585144"
height="29.6516"
x="117.60985"
y="82.473022" />
<rect
style="color:#000000;fill:none;stroke:#0054ff;stroke-width:0.625;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
id="rect2985-7-4"
width="72.585144"
height="29.6516"
x="200.92497"
y="51.917603" />
<rect
style="color:#000000;fill:none;stroke:#fff400;stroke-width:0.625;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
id="rect2985-7-0"
width="72.585144"
height="29.6516"
x="42.5"
y="113.02844" />
<rect
style="color:#000000;fill:none;stroke:#ff00de;stroke-width:0.625;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
id="rect2985-7-0-5"
width="72.585144"
height="29.6516"
x="123.29042"
y="143.58386" />
<rect
style="color:#000000;fill:none;stroke:#ff7100;stroke-width:0.625;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
id="rect2985-7-0-5-5"
width="72.585144"
height="29.6516"
x="221.75375"
y="174.13928" />
<text
xml:space="preserve"
style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:LMRoman12;-inkscape-font-specification:LMRoman12"
x="255.11232"
y="193.33777"
id="text3857"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan3859"
x="255.11232"
y="193.33777">0</tspan></text>
<text
xml:space="preserve"
style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:LMRoman12;-inkscape-font-specification:LMRoman12"
x="156.577"
y="162.39966"
id="text3861"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan3863"
x="156.577"
y="162.39966">1</tspan></text>
<text
xml:space="preserve"
style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:LMRoman12;-inkscape-font-specification:LMRoman12"
x="75.858574"
y="131.84424"
id="text3865"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan3867"
x="75.858574"
y="131.84424">2</tspan></text>
<text
xml:space="preserve"
style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:LMRoman12;-inkscape-font-specification:LMRoman12"
x="150.96841"
y="101.16282"
id="text3869"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan3871"
x="150.96841"
y="101.16282">3</tspan></text>
<text
xml:space="preserve"
style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:LMRoman12;-inkscape-font-specification:LMRoman12"
x="234.28354"
y="70.793404"
id="text3873"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan3875"
x="234.28354"
y="70.793404">4</tspan></text>
<text
xml:space="preserve"
style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:LMRoman12;-inkscape-font-specification:LMRoman12"
x="111.51997"
y="40.045986"
id="text3877"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan3879"
x="111.51997"
y="40.045986">5</tspan></text>
<path
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-end:url(#Arrow2Mend)"
d="m 324.24253,185.09612 0,-149.300453"
id="path3881"
inkscape:connector-curvature="0" />
<text
xml:space="preserve"
style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:LMRoman12;-inkscape-font-specification:LMRoman12"
x="330.85974"
y="98.245438"
id="text4327"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan4329"
x="330.85974"
y="98.245438">increasing</tspan><tspan
sodipodi:role="line"
x="330.85974"
y="113.24544"
id="tspan4331">layering</tspan><tspan
sodipodi:role="line"
x="330.85974"
y="128.24544"
id="tspan4333">order</tspan></text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 8.5 KiB

View File

@ -0,0 +1,240 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<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="744.09448819"
height="1052.3622047"
id="svg2"
version="1.1"
inkscape:version="0.48.2 r9819"
sodipodi:docname="layer-order-2.svg">
<defs
id="defs4">
<marker
inkscape:stockid="Arrow2Mend"
orient="auto"
refY="0.0"
refX="0.0"
id="Arrow2Mend"
style="overflow:visible;">
<path
id="path3913"
style="font-size:12.0;fill-rule:evenodd;stroke-width:0.62500000;stroke-linejoin:round;"
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
transform="scale(0.6) rotate(180) translate(0,0)" />
</marker>
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="2.4179431"
inkscape:cx="170.5"
inkscape:cy="941"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:window-width="1280"
inkscape:window-height="949"
inkscape:window-x="0"
inkscape:window-y="25"
inkscape:window-maximized="1" />
<metadata
id="metadata7">
<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
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<g
id="g4432">
<g
id="g4403">
<rect
style="color:#000000;fill:none;stroke:#ff7100;stroke-width:0.625;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
id="rect2985-7-0-5-5"
width="72.585144"
height="29.6516"
x="221.75375"
y="174.13928" />
<text
xml:space="preserve"
style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:LMRoman12;-inkscape-font-specification:LMRoman12"
x="255.11232"
y="193.33777"
id="text3857"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan3859"
x="255.11232"
y="193.33777">0</tspan></text>
</g>
<g
transform="translate(0,30.55542)"
id="g4398">
<rect
style="color:#000000;fill:none;stroke:#ff00de;stroke-width:0.625;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
id="rect2985-7-0-5"
width="72.585144"
height="29.6516"
x="123.29042"
y="143.58386" />
<text
xml:space="preserve"
style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:LMRoman12;-inkscape-font-specification:LMRoman12"
x="156.577"
y="162.39966"
id="text3861"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan3863"
x="156.577"
y="162.39966">1</tspan></text>
</g>
<g
transform="translate(0,61.11084)"
id="g4393">
<rect
style="color:#000000;fill:none;stroke:#fff400;stroke-width:0.625;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
id="rect2985-7-0"
width="72.585144"
height="29.6516"
x="42.5"
y="113.02844" />
<text
xml:space="preserve"
style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:LMRoman12;-inkscape-font-specification:LMRoman12"
x="75.858574"
y="131.84424"
id="text3865"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan3867"
x="75.858574"
y="131.84424">2</tspan></text>
</g>
</g>
<g
id="g4421"
transform="translate(0,0.38900757)">
<g
transform="translate(0,55.00543)"
id="g4388">
<rect
style="color:#000000;fill:none;stroke:#ff0003;stroke-width:0.625;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
id="rect2985-7"
width="72.585144"
height="29.6516"
x="117.60985"
y="82.473022" />
<text
xml:space="preserve"
style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:LMRoman12;-inkscape-font-specification:LMRoman12"
x="150.96841"
y="101.16282"
id="text3869"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan3871"
x="150.96841"
y="101.16282">3</tspan></text>
</g>
<g
transform="translate(0,85.56085)"
id="g4382">
<rect
style="color:#000000;fill:none;stroke:#0054ff;stroke-width:0.625;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
id="rect2985-7-4"
width="72.585144"
height="29.6516"
x="200.92497"
y="51.917603" />
<g
id="g4378">
<text
sodipodi:linespacing="125%"
id="text3873"
y="70.793404"
x="234.28354"
style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:LMRoman12;-inkscape-font-specification:LMRoman12"
xml:space="preserve"><tspan
y="70.793404"
x="234.28354"
id="tspan3875"
sodipodi:role="line">4</tspan></text>
</g>
</g>
</g>
<g
id="g4446">
<g
transform="translate(0,80.233484)"
id="g4414">
<g
id="g4408">
<g
id="g4373">
<rect
style="color:#000000;fill:none;stroke:#00ff00;stroke-width:0.625;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
id="rect2985"
width="72.585144"
height="29.6516"
x="78.1614"
y="21.362185" />
<text
xml:space="preserve"
style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:LMRoman12;-inkscape-font-specification:LMRoman12"
x="111.51997"
y="40.045986"
id="text3877"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan3879"
x="111.51997"
y="40.045986">5</tspan></text>
</g>
</g>
</g>
</g>
<path
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-end:url(#Arrow2Mend)"
d="m 324.24253,185.09612 0,-78.57919"
id="path3881"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<text
xml:space="preserve"
style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:LMRoman12;-inkscape-font-specification:LMRoman12"
x="330.85974"
y="140.01648"
id="text4327"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan4329"
x="330.85974"
y="140.01648">increasing</tspan><tspan
sodipodi:role="line"
x="330.85974"
y="155.01648"
id="tspan4333">layer</tspan></text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 9.7 KiB

View File

@ -1,19 +1,19 @@
\documentclass{article}
\title{Region Layering}
\title{Region layering}
\author{}
\date{}
\usepackage{graphicx}
\usepackage{graphicx,amsmath}
\begin{document}
\maketitle
\section{Introduction}
When regions overlap in time, we need to decide which one should be
played. Ardour has a few options to set how this decision is made.
played.
\subsection{Layers}
\section{Layers}
Each region on a playlist is on a \emph{layer}. All overlapping regions
are on a unique layer, and when overlaps exist the highest-layered
@ -37,114 +37,168 @@ This follows the basic rule that, at any given point, the region on
the highest layer will be played.
\section{Choice of layering}
\section{Which layer does a region go on?}
There are two main decisions to be made with regards to how a playlist should be layered:
\begin{itemize}
\item Given overlapping regions, what order should they be layered in?
\item When should layering be changed?
\end{itemize}
The logic to decide which layer a region goes onto is somewhat complicated.
This section describes it in hand-wavey and more technical terms.
\subsection{Layering order}
\subsection{Hand-wavey description}
Ardour provides three-and-a-half ways to decide on the order in which
regions are layered. The most basic choice is:
\begin{itemize}
\item \emph{Later is higher} --- regions which are later in time will
be on higher layers.
\item \emph{Most recently added is higher} --- regions which were more
recently added to the playlist will be on higher layers.
\item \emph{Most recently edited or added is higher} --- regions which
were more recently edited or added to the playlist will be on
higher layers.
\end{itemize}
This choice can be set per-session from the \emph{Session Properties} dialogue
box.
\subsubsection{Explicit ordering}
There are also cases when none of these rules should apply. If, for
example, you want to put a given region at the top of the stack (on
the highest layer), this is possible using the region `raise to top'
command. Following such a command (called an `explicit layering'),
the regions on the playlist may no longer obey any of the standard
ordering rules.
This situation also arises when editing tracks using the `stacked' layer mode.
In this mode, almost all layering is explicit. When starting a region drag,
the other regions on a track spread apart vertically to allow the dragged
region to be dropped in any position within the region stack. The normal
layering rules will only be followed if a region is dropped on top of another;
in all other cases, explicit layering will be used to put the region wherever
it was dropped.
\subsection{When to update layering}
There are two distinct approaches to updating layering:
\begin{itemize}
\item Update whenever any region edit is performed.
\item Update only when a region is edited such that a new overlap has been set up.
\end{itemize}
The approach to use is optional, and can be set in \emph{Session Properties}.
This decision only has consequences when an explicit layering command has
been used. Consider the case in Figure~\ref{fig:explicit-layering1}.
A playlist maintains an internal \emph{layering order} for regions. This order
is not directly visible in Ardour, but it's useful to understand it
nonetheless. Figure~\ref{fig:layering-order-1} gives a rough idea of what this
means.
\begin{figure}[ht]
\begin{center}
\includegraphics{explicit-layering1.pdf}
\includegraphics{layering-order-1.pdf}
\end{center}
\caption{Explicit layering: stage 1}
\label{fig:explicit-layering1}
\caption{Layering order}
\label{fig:layering-order-1}
\end{figure}
Given that arrangement, imagine that we perform a `raise to top' on region $C$.
This results in the arrangement in Figure~\ref{fig:explicit-layering2}.
Here we see 6 regions; as the layering order value increases, the region will
be placed on a higher layer.
Every time any region is moved, added or edited, a \emph{relayer} occurs. This
collapses the regions down into layers. For our example, this would result in
the arrangement in Figure~\ref{fig:layering-order-2}.
\begin{figure}[ht]
\begin{center}
\includegraphics{explicit-layering2.pdf}
\includegraphics{layering-order-2.pdf}
\end{center}
\caption{Explicit layering: stage 2}
\label{fig:explicit-layering2}
\caption{Layering}
\label{fig:layering-order-2}
\end{figure}
Imagine now that region $C$ is moved very slightly to the left, so
that it still overlaps both $A$ and $B$. If we are updating whenever
any region edit is performed, this will result in a relayer; the
regions' arrangement will go back to that in
Figure~\ref{fig:explicit-layering1}.
The relayer operation takes each region, in the layering order, and puts it
on the lowest possible layer that it can be on without overlap.
If, on the other hand, we only relayer when a new overlap is set up,
the region layering will remain as in
Figure~\ref{fig:explicit-layering2}. Before the edit, regions $A$,
$B$ and $C$ overlapped; after the edit, the situation is the same, so
no relayering is performed.
Another, more complex, example is shown in Figure~\ref{fig:tricky-explicit-layering}.
\subsubsection{Layering order}
Given that arrangement, the remaining question is how the layering order is
arrived at. The rules are as follows:
\begin{itemize}
\item When a region is added to a playlist, it goes above the current highest
region in the layering order.
\item In `overlaid' track mode, moving or editing regions does not change the
layering order. Hence, moving regions about will maintain their position in
the layering order. Changing overlaps may change the \emph{layer} that the
region ends up on, but not the order in which they will be layered.
\item In `stacked' track mode, moving regions places the region on the layer
that they are dropped on. This is achieved by modifying the layering order
for the region that is moved, so that when the relayer operation happens the
region ends up on the desired layer.
\item When regions are `raised' or `lowered' in the stack, the layering order
is modified to achieve the desired layer change.
\end{itemize}
The upshot of all this is that regions should maintain their expected layering
order, unless that order is explicitly change using `stacked' mode or by
explicit layering commands like `raise' or `lower'.
\subsection{Technical description}
Each region on a playlist has three layering-related properties: its current
layer $c$ (an integer) and its layering index $i$ (also an integer). It also
has an \emph{optional} pending layer $p$ which is fractional.
Whenever a region is added, moved, trimmed, etc.\ we run a \emph{relayer}. This
does the following:
\begin{enumerate}
\item Take a list of all regions and remove those who have a value for $p$.
\item Sort the remainder in ascending order of $i$.
\item Insert the regions which have a value for $p$ in the correct place in the
list by comparing $c$ of those in the list to $p$ of the inserted region.
\item Iterate over the resulting list, putting each region on the lowest available
layer, setting its current layer $c$, and clearing $p$.
\item If any region had a pending layer, iterate through the region list again
giving each region a new layering index $i$ ascending from 0.
\end{enumerate}
The pending layer $p$ is set up in the following situations:
\begin{enumerate}
\item When a region is added to the playlist, $p$ is set to $\infty$.
\item When a region is raised to the top of the playlist, $p$ is set to $\infty$.
\item When a region is raised one step in the playlist, $p$ is set to $c + 1.5$.
\item When a region is lowered to the bottom of the playlist, $p$ is set to $-0.5$.
\item When a region is lowered one step int the playlist, $p$ is set to $c - 1.5$.
\item When a region is explicitly put between layers $A$ and $B$ in `stacked'
mode, $p$ is set to $(A + B) / 2$.
\end{enumerate}
The idea of this approach is that the layering indices $i$ are used to keep a
current state of the stack, and this state is used to maintain region
relationships. Setting $p$ will alter these relationships, after which the
layering indices $i$ are updated to reflect the new status quo.
It is not sufficient to use current layer $c$ as the state of the stack.
Consider two overlapping regions $P$ and $Q$, with $P$ on layer~0 and $Q$ on
layer~1. Now raise $P$ to the top of the stack, so that $Q$ is on layer~0 and
$P$ on layer~1. Move $P$ away from $Q$ (in overlaid mode) so that both regions
are on layer~0. Now drag $P$ back over $Q$. One would expect $P$ to return to
the top of the stack, since it was explicitly raised earlier. However, if the
relayer operation were to compare $c$ for each region, they would be identical;
the information that $P$ was once higher than $Q$ has been lost.
\section{Stacked mode}
When a track is being displayed in \emph{stacked} mode, regions are spread out
vertically to indicate their layering, like in Figure~\ref{fig:stacked}.
\begin{figure}[ht]
\begin{center}
\includegraphics{tricky-explicit-layering.pdf}
\includegraphics[scale=0.5]{stacked.png}
\end{center}
\caption{More complex explicit layering}
\label{fig:tricky-explicit-layering}
\caption{A track in stacked mode}
\label{fig:stacked}
\end{figure}
% XXX: this makes no sense
In this mode, layering is performed \emph{explicitly}. In other words, the
user's immediate actions decide which layer a region should be put on. When a
region move drag is started in stacked mode, the regions separate further out
vertically, to leave space between each layer, as shown in
Figure~\ref{fig:stacked-drag}.
Here, imagine that $C$ has been moved to the top of the stack with an explicit
`raise to top' command. Now consider an extension of $C$ so that its
right-hand edge overlaps $D$. If we are relayering only on new overlaps, this
case presents one new overlap (that of $C$ with $D$). In this case, $C$ is
moved according to the current layering rules so that it is correct with
respect to $D$. In addition, $A$ and $B$ are re-layered so that the relation
of $C$ to $A$ and $B$ is preserved.
\begin{figure}[ht]
\begin{center}
\includegraphics[scale=0.5]{stacked-drag.png}
\end{center}
\caption{A track in stacked mode during a drag}
\label{fig:stacked-drag}
\end{figure}
The region(s) being dragged can then be dropped in any location, horizontally
and vertically, and the regions will be layered accordingly.
\section{Overlaid mode}
When a track is being displayed in \emph{overlaid} mode, regions are
displayed on top of one another, like in Figure~\ref{fig:overlaid}.
\begin{figure}[ht]
\begin{center}
\includegraphics[scale=0.5]{overlaid.png}
\end{center}
\caption{A track in overlaid mode}
\label{fig:overlaid}
\end{figure}
In this mode, drags of regions maintain the same \emph{layer ordering}, even if the layers may
change.
\end{document}

View File

@ -1,323 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<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="744.09448819"
height="1052.3622047"
id="svg2"
version="1.1"
inkscape:version="0.47 r22583"
sodipodi:docname="tricky-explicit-layering1.svg">
<defs
id="defs4">
<marker
inkscape:stockid="Arrow2Lend"
orient="auto"
refY="0.0"
refX="0.0"
id="Arrow2Lend"
style="overflow:visible;">
<path
id="path3618"
style="font-size:12.0;fill-rule:evenodd;stroke-width:0.62500000;stroke-linejoin:round;"
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
transform="scale(1.1) rotate(180) translate(1,0)" />
</marker>
<inkscape:perspective
sodipodi:type="inkscape:persp3d"
inkscape:vp_x="0 : 526.18109 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_z="744.09448 : 526.18109 : 1"
inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
id="perspective10" />
<inkscape:perspective
id="perspective4058"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective4089"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective4120"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective4151"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective4365"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective4386"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<marker
inkscape:stockid="Arrow2Lend"
orient="auto"
refY="0"
refX="0"
id="Arrow2Lend-6"
style="overflow:visible">
<path
id="path3618-4"
style="font-size:12px;fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
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="matrix(-1.1,0,0,-1.1,-1.1,0)" />
</marker>
<inkscape:perspective
id="perspective4449"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<marker
inkscape:stockid="Arrow2Lend"
orient="auto"
refY="0"
refX="0"
id="Arrow2Lend-5"
style="overflow:visible">
<path
id="path3618-1"
style="font-size:12px;fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
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="matrix(-1.1,0,0,-1.1,-1.1,0)" />
</marker>
<inkscape:perspective
id="perspective4668"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective4696"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective4696-1"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="2.633643"
inkscape:cx="191.36241"
inkscape:cy="670.78783"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="true"
inkscape:window-width="1280"
inkscape:window-height="949"
inkscape:window-x="0"
inkscape:window-y="25"
inkscape:window-maximized="1"
gridtolerance="10">
<inkscape:grid
type="xygrid"
id="grid2816"
empspacing="5"
visible="true"
enabled="true"
snapvisiblegridlinesonly="true"
units="mm"
spacingx="5mm"
spacingy="5mm" />
</sodipodi:namedview>
<metadata
id="metadata7">
<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
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<path
style="fill:none;stroke:#000000;stroke-width:0.62500000000000000;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-end:url(#Arrow2Lend);color:#000000;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
d="m 88.582677,432.28344 159.448823,0"
id="path3592" />
<text
xml:space="preserve"
style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#000000;fill-opacity:1;stroke:none;font-family:LMRoman12;-inkscape-font-specification:LMRoman12"
x="255.20872"
y="435.23877"
id="text4042"><tspan
sodipodi:role="line"
id="tspan4044"
x="255.20872"
y="435.23877">time</tspan></text>
<rect
style="color:#000000;fill:none;stroke:#000000;stroke-width:0.625;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
id="rect2818"
width="106.29921"
height="35.433071"
x="88.582695"
y="343.70078" />
<rect
style="color:#000000;fill:none;stroke:#000000;stroke-width:0.625;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
id="rect2818-2"
width="88.582687"
height="35.433071"
x="124.01575"
y="379.13385" />
<rect
style="color:#000000;fill:none;stroke:#000000;stroke-width:0.625;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
id="rect2818-2-2-4"
width="70.866142"
height="35.433071"
x="230.31496"
y="379.13385" />
<text
xml:space="preserve"
style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#000000;fill-opacity:1;stroke:none;font-family:LMRoman12;-inkscape-font-specification:LMRoman12"
x="137.33429"
y="365.70132"
id="text4320"><tspan
sodipodi:role="line"
id="tspan4322"
x="137.33429"
y="365.70132">A</tspan></text>
<text
xml:space="preserve"
style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#000000;fill-opacity:1;stroke:none;font-family:LMRoman12;-inkscape-font-specification:LMRoman12"
x="164.22108"
y="400.94839"
id="text4324"><tspan
sodipodi:role="line"
id="tspan4326"
x="164.22108"
y="400.94839">B</tspan></text>
<rect
style="color:#000000;fill:none;stroke:#000000;stroke-width:0.625;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
id="rect2818-2-2"
width="44.545933"
height="35.433071"
x="177.16534"
y="308.2677" />
<text
xml:space="preserve"
style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#000000;fill-opacity:1;stroke:none;font-family:LMRoman12;-inkscape-font-specification:LMRoman12"
x="195.20232"
y="330.08224"
id="text4328"><tspan
sodipodi:role="line"
id="tspan4330"
x="195.20232"
y="330.08224">C</tspan></text>
<text
xml:space="preserve"
style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#000000;fill-opacity:1;stroke:none;font-family:LMRoman12;-inkscape-font-specification:LMRoman12"
x="261.33801"
y="400.94839"
id="text4332"><tspan
sodipodi:role="line"
id="tspan4334"
x="261.33801"
y="400.94839">D</tspan></text>
<path
style="color:#000000;fill:none;stroke:#000000;stroke-width:0.625;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;marker-end:url(#Arrow2Lend);visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
d="m 318.89764,432.28344 0,-124.01575"
id="path3592-8"
sodipodi:nodetypes="cc" />
<text
xml:space="preserve"
style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#000000;fill-opacity:1;stroke:none;font-family:LMRoman12;-inkscape-font-specification:LMRoman12"
x="307.30875"
y="301.75519"
id="text4406"><tspan
sodipodi:role="line"
id="tspan4408"
x="307.30875"
y="301.75519">layer</tspan></text>
<path
style="fill:none;stroke:#000000;stroke-width:0.62500000000000000;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;color:#000000;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;marker:none;marker-end:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
d="m 311.84462,397.273 13.54507,0"
id="path4439" />
<text
xml:space="preserve"
style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#000000;fill-opacity:1;stroke:none;font-family:LMRoman12;-inkscape-font-specification:LMRoman12"
x="328.09393"
y="401.58279"
id="text4656"><tspan
sodipodi:role="line"
id="tspan4658"
x="328.09393"
y="401.58279">0</tspan></text>
<path
style="color:#000000;fill:none;stroke:#000000;stroke-width:0.625;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
d="m 312.32197,361.87109 13.54507,0"
id="path4439-1" />
<text
xml:space="preserve"
style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#000000;fill-opacity:1;stroke:none;font-family:LMRoman12;-inkscape-font-specification:LMRoman12"
x="328.02194"
y="366.18088"
id="text4656-0-0"><tspan
sodipodi:role="line"
id="tspan4658-6-3"
x="328.02194"
y="366.18088">1</tspan></text>
<path
style="color:#000000;fill:none;stroke:#000000;stroke-width:0.625;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
d="m 311.84462,325.98423 13.54507,0"
id="path4439-1-6" />
<text
xml:space="preserve"
style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#000000;fill-opacity:1;stroke:none;font-family:LMRoman12;-inkscape-font-specification:LMRoman12"
x="328.09393"
y="329.49646"
id="text4742"><tspan
sodipodi:role="line"
id="tspan4744"
x="328.09393"
y="329.49646">2</tspan></text>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 13 KiB

View File

@ -812,7 +812,7 @@ AudioStreamView::update_content_height (CrossfadeView* cv)
switch (_layer_display) {
case Overlaid:
cv->set_y (0);
cv->set_height (height);
cv->set_heights (height, height);
break;
case Stacked:
@ -827,10 +827,10 @@ AudioStreamView::update_content_height (CrossfadeView* cv)
if (_layer_display == Stacked) {
cv->set_y ((_layers - high - 1) * h);
cv->set_height ((high - low + 1) * h);
cv->set_heights ((high - low + 1) * h, h);
} else {
cv->set_y (((_layers - high) * 2 - 1) * h);
cv->set_height (((high - low) * 2 + 1) * h);
cv->set_heights (((high - low) * 2 + 1) * h, h);
}
}
}

View File

@ -57,7 +57,8 @@ CrossfadeView::CrossfadeView (ArdourCanvas::Group *parent,
crossfade (xf),
left_view (lview),
right_view (rview),
_all_in_view (false)
_all_in_view (false),
_child_height (0)
{
_valid = true;
_visible = true;
@ -109,13 +110,15 @@ CrossfadeView::reset_width_dependent_items (double pixel_width)
}
void
CrossfadeView::set_height (double h)
CrossfadeView::set_heights (double fade_height, double child_height)
{
if (h > TimeAxisView::preset_height (HeightSmall)) {
h -= NAME_HIGHLIGHT_SIZE;
if (child_height > TimeAxisViewItem::NAME_HIGHLIGHT_THRESH) {
fade_height -= NAME_HIGHLIGHT_SIZE;
child_height -= NAME_HIGHLIGHT_SIZE;
}
TimeAxisViewItem::set_height (h);
TimeAxisViewItem::set_height (fade_height);
_child_height = child_height;
redraw_curves ();
}
@ -203,7 +206,9 @@ CrossfadeView::redraw_curves ()
for (int i = 0, pci = 0; i < npoints; ++i) {
Art::Point &p = (*points)[pci++];
p.set_x (xoff + i + 1);
p.set_y (_height - ((_height - 2) * vec[i]));
double const ho = crossfade->in()->layer() > crossfade->out()->layer() ? _child_height : _height;
p.set_y (ho - ((_child_height - 2) * vec[i]));
}
fade_in->property_points() = *points;
@ -213,7 +218,9 @@ CrossfadeView::redraw_curves ()
for (int i = 0, pci = 0; i < npoints; ++i) {
Art::Point &p = (*points)[pci++];
p.set_x (xoff + i + 1);
p.set_y (_height - ((_height - 2) * vec[i]));
double const ho = crossfade->in()->layer() < crossfade->out()->layer() ? _child_height : _height;
p.set_y (ho - ((_child_height - 2) * vec[i]));
}
fade_out->property_points() = *points;

View File

@ -48,7 +48,7 @@ public:
AudioRegionView& left_view; // and these too
AudioRegionView& right_view;
void set_height (double);
void set_heights (double, double);
bool valid() const { return _valid; }
bool visible() const { return _visible; }
@ -68,6 +68,7 @@ private:
bool _valid;
bool _visible;
bool _all_in_view;
double _child_height;
ArdourCanvas::Line *fade_in;
ArdourCanvas::Line *fade_out;

View File

@ -2475,7 +2475,7 @@ Editor::get_state ()
/** @param y y offset from the top of all trackviews.
* @return pair: TimeAxisView that y is over, layer index.
* TimeAxisView may be 0. Layer index is the layer number if the TimeAxisView is valid and is
* in stacked region display mode, otherwise 0.
* in stacked or expanded region display mode, otherwise 0.
*/
std::pair<TimeAxisView *, double>
Editor::trackview_by_y_position (double y)

View File

@ -488,10 +488,6 @@ RegionMotionDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (_drags->current_pointer_y ());
_last_pointer_time_axis_view = find_time_axis_view (tv.first);
_last_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
if (tv.first->view()->layer_display() == Stacked) {
tv.first->view()->set_layer_display (Expanded);
}
}
double
@ -578,7 +574,6 @@ RegionMotionDrag::y_movement_allowed (int delta_track, double delta_layer) const
/* Note that we allow layer to be up to 0.5 below zero, as this is used by `Expanded'
mode to allow the user to place a region below another on layer 0.
*/
if (delta_track == 0 && (l < -0.5 || l >= int (to->view()->layers()))) {
/* Off the top or bottom layer; note that we only refuse if the track hasn't changed.
If it has, the layers will be munged later anyway, so it's ok.
@ -599,6 +594,10 @@ RegionMotionDrag::motion (GdkEvent* event, bool first_move)
/* Find the TimeAxisView that the pointer is now over */
pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (_drags->current_pointer_y ());
if (first_move && tv.first->view()->layer_display() == Stacked) {
tv.first->view()->set_layer_display (Expanded);
}
/* Bail early if we're not over a track */
RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv.first);
if (!rtv || !rtv->is_track()) {
@ -646,21 +645,21 @@ RegionMotionDrag::motion (GdkEvent* event, bool first_move)
if (first_move) {
rv->get_time_axis_view().hide_dependent_views (*rv);
/* Absolutely no idea why this is necessary, but it is; without
it, the region view disappears after the reparent.
*/
_editor->update_canvas_now ();
/* Reparent to a non scrolling group so that we can keep the
region selection above all time axis views.
Reparenting means that we will have to move the region view
later, as the two parent groups have different coordinates.
*/
rv->get_canvas_group()->reparent (*(_editor->_region_motion_group));
rv->fake_set_opaque (true);
if (!rv->get_time_axis_view().hidden()) {
/* the track that this region view is on is hidden, so hide the region too */
rv->get_canvas_group()->hide ();
}
}
/* If we have moved tracks, we'll fudge the layer delta so that the
@ -680,12 +679,12 @@ RegionMotionDrag::motion (GdkEvent* event, bool first_move)
if (tv->view()->layer_display() == Stacked) {
tv->view()->set_layer_display (Expanded);
}
/* We're only allowed to go -ve in layer on Expanded views */
if (tv->view()->layer_display() != Expanded && (i->layer + this_delta_layer) < 0) {
this_delta_layer = - i->layer;
}
/* Set height */
rv->set_height (tv->view()->child_height ());
@ -826,12 +825,13 @@ RegionMotionDrag::finished (GdkEvent *, bool)
(*i)->view()->set_layer_display (Stacked);
}
}
}
}
void
RegionMoveDrag::finished (GdkEvent* ev, bool movement_occurred)
{
RegionMotionDrag::finished (ev, movement_occurred);
if (!movement_occurred) {
/* just a click */
return;
@ -965,9 +965,6 @@ RegionMoveDrag::finished_no_copy (
RegionSelection new_views;
PlaylistSet modified_playlists;
PlaylistSet frozen_playlists;
list<pair<boost::shared_ptr<Region>, double> > pending_explicit_relayers;
Playlist::RegionList pending_implicit_relayers;
set<RouteTimeAxisView*> views_to_update;
if (_brushing) {
@ -1049,12 +1046,8 @@ RegionMoveDrag::finished_no_copy (
boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
bool const explicit_relayer = dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded;
if (explicit_relayer) {
pending_explicit_relayers.push_back (make_pair (rv->region (), dest_layer));
} else {
pending_implicit_relayers.push_back (rv->region ());
if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
rv->region()->set_pending_layer (dest_layer);
}
/* freeze playlist to avoid lots of relayering in the case of a multi-region drag */
@ -1063,7 +1056,6 @@ RegionMoveDrag::finished_no_copy (
if (r.second) {
playlist->freeze ();
playlist->suspend_relayer ();
}
/* this movement may result in a crossfade being modified, so we need to get undo
@ -1119,24 +1111,10 @@ RegionMoveDrag::finished_no_copy (
_editor->selection->set (new_views);
}
/* We can't use the normal mechanism for relayering, as some regions may require an explicit relayer
rather than an implicit one. So we thaw before resuming relayering, then do the relayers
that we require.
*/
for (PlaylistSet::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
(*p)->thaw();
(*p)->resume_relayer ();
}
for (list<pair<boost::shared_ptr<Region>, double> >::iterator i = pending_explicit_relayers.begin(); i != pending_explicit_relayers.end(); ++i) {
i->first->playlist()->relayer (i->first, i->second);
}
for (Playlist::RegionList::iterator i = pending_implicit_relayers.begin(); i != pending_implicit_relayers.end(); ++i) {
(*i)->playlist()->relayer (*i);
}
/* write commands for the accumulated diffs for all our modified playlists */
add_stateful_diff_commands_for_playlists (modified_playlists);
@ -1144,11 +1122,11 @@ RegionMoveDrag::finished_no_copy (
/* We have futzed with the layering of canvas items on our streamviews.
If any region changed layer, this will have resulted in the stream
views being asked to set up their region views, and all will be
well. If not, we might now have badly-ordered region views. Ask
the Streamviews involved to sort themselves out, just in case.
views being asked to set up their region views, and all will be well.
If not, we might now have badly-ordered region views. Ask the StreamViews
involved to sort themselves out, just in case.
*/
for (set<RouteTimeAxisView*>::iterator i = views_to_update.begin(); i != views_to_update.end(); ++i) {
(*i)->view()->playlist_layered ((*i)->track ());
}
@ -1214,7 +1192,7 @@ RegionMoveDrag::insert_region_into_playlist (
dest_playlist->add_region (region, where);
if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
dest_playlist->relayer (region, dest_layer);
region->set_pending_layer (dest_layer);
}
c.disconnect ();
@ -1268,7 +1246,7 @@ RegionMotionDrag::aborted (bool)
(*i)->view()->set_layer_display (Stacked);
}
}
for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
RegionView* rv = i->view;
TimeAxisView* tv = &(rv->get_time_axis_view ());

View File

@ -249,7 +249,7 @@ struct DraggingView
* or -1 if it is not visible.
*/
int time_axis_view;
/** Layer that this region is currently being displayed on. This is a double
/** layer that this region is currently being displayed on. This is a double
rather than a layer_t as we use fractional layers during drags to allow the user
to indicate a new layer to put a region on.
*/

View File

@ -262,29 +262,6 @@ SessionOptionEditor::SessionOptionEditor (Session* s)
/* Misc */
add_option (_("Misc"), new OptionEditorHeading (_("Layering (in overlaid mode)")));
ComboOption<LayerModel>* lm = new ComboOption<LayerModel> (
"layer-model",
_("Layering model"),
sigc::mem_fun (*_session_config, &SessionConfiguration::get_layer_model),
sigc::mem_fun (*_session_config, &SessionConfiguration::set_layer_model)
);
lm->add (LaterHigher, _("later is higher"));
lm->add (AddOrBoundsChangeHigher, _("most recently edited or added is higher"));
lm->add (AddHigher, _("most recently added is higher"));
add_option (_("Misc"), new BoolOption (
"relayer-on-all-edits",
_("Relayer regions after every edit"),
sigc::mem_fun (*_session_config, &SessionConfiguration::get_relayer_on_all_edits),
sigc::mem_fun (*_session_config, &SessionConfiguration::set_relayer_on_all_edits)
));
add_option (_("Misc"), lm);
add_option (_("Misc"), new OptionEditorHeading (_("MIDI Options")));
add_option (_("Misc"), new BoolOption (

View File

@ -181,7 +181,7 @@ StreamView::add_region_view (boost::weak_ptr<Region> wr)
add_region_view_internal (r, true);
if (_layer_display == Stacked) {
if (_layer_display == Stacked || _layer_display == Expanded) {
update_contents_height ();
}
}
@ -301,7 +301,7 @@ StreamView::playlist_layered (boost::weak_ptr<Track> wtr)
_layers = tr->playlist()->top_layer() + 1;
}
if (_layer_display == Stacked || _layer_display == Expanded) {
if (_layer_display == Stacked) {
update_contents_height ();
update_coverage_frames ();
} else {
@ -574,7 +574,7 @@ StreamView::child_height () const
case Expanded:
return height / (_layers * 2 + 1);
}
/* NOTREACHED */
return height;
}

View File

@ -1196,7 +1196,7 @@ TimeAxisView::color_handler ()
* If the covering object is a child axis, then the child is returned.
* TimeAxisView is 0 otherwise.
* Layer index is the layer number (possibly fractional) if the TimeAxisView is valid
* and is in stacked or expanded region display mode, otherwise 0.
* and is in stacked or expanded * region display mode, otherwise 0.
*/
std::pair<TimeAxisView*, double>
TimeAxisView::covers_y_position (double y)
@ -1215,7 +1215,7 @@ TimeAxisView::covers_y_position (double y)
case Stacked:
if (view ()) {
/* compute layer */
l = floor ((_y_position + height - y) / (view()->child_height ()));
l = layer_t ((_y_position + height - y) / (view()->child_height ()));
/* clamp to max layers to be on the safe side; sometimes the above calculation
returns a too-high value */
if (l >= view()->layers ()) {
@ -1225,7 +1225,7 @@ TimeAxisView::covers_y_position (double y)
break;
case Expanded:
if (view ()) {
int n = floor ((_y_position + height - y) / (view()->child_height ()));
int const n = floor ((_y_position + height - y) / (view()->child_height ()));
l = n * 0.5 - 0.5;
if (l >= (view()->layers() - 0.5)) {
l = view()->layers() - 0.5;

View File

@ -44,9 +44,6 @@
#include "ardour/session_object.h"
#include "ardour/data_type.h"
class PlaylistOverlapCacheTest;
class PlaylistLayeringTest;
namespace ARDOUR {
class Session;
@ -225,10 +222,7 @@ public:
framepos_t find_next_top_layer_position (framepos_t) const;
uint32_t combine_ops() const { return _combine_ops; }
void relayer (boost::shared_ptr<Region>);
void relayer (boost::shared_ptr<Region>, double);
void suspend_relayer ();
void resume_relayer ();
uint64_t highest_layering_index () const;
protected:
friend class Session;
@ -355,7 +349,7 @@ public:
boost::shared_ptr<Playlist> cut (framepos_t start, framecnt_t cnt, bool result_is_hidden);
boost::shared_ptr<Playlist> copy (framepos_t start, framecnt_t cnt, bool result_is_hidden);
void relayer (RegionList const &);
void relayer ();
void begin_undo ();
void end_undo ();
@ -382,71 +376,6 @@ public:
with its constituent regions
*/
virtual void pre_uncombine (std::vector<boost::shared_ptr<Region> >&, boost::shared_ptr<Region>) {}
private:
friend class ::PlaylistOverlapCacheTest;
friend class ::PlaylistLayeringTest;
/** A class which is used to store temporary (fractional)
* layer assignments for some regions.
*/
class TemporaryLayers
{
public:
void set (boost::shared_ptr<Region>, double);
double get (boost::shared_ptr<Region>) const;
private:
typedef std::map<boost::shared_ptr<Region>, double> Map;
Map _map;
};
/** Class to sort by temporary layer, for use with std::list<>::sort() */
class SortByTemporaryLayer
{
public:
SortByTemporaryLayer (TemporaryLayers const & t)
: _temporary_layers (t) {}
bool operator() (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) const {
return _temporary_layers.get (a) < _temporary_layers.get (b);
}
private:
Playlist::TemporaryLayers const & _temporary_layers;
};
/** A cache of what overlaps what, for a given playlist in a given state.
* Divides a playlist up into time periods and notes which regions cover those
* periods, so that get() is reasonably quick.
*/
class OverlapCache
{
public:
OverlapCache (Playlist *);
RegionList get (Evoral::Range<framepos_t>) const;
private:
std::pair<int, int> cache_indices (Evoral::Range<framepos_t>) const;
double _division_size;
std::vector<RegionList> _cache;
Evoral::Range<framepos_t> _range;
static int const _divisions;
};
TemporaryLayers compute_temporary_layers (RegionList const &);
void commit_temporary_layers (TemporaryLayers const &);
RegionList recursive_regions_touched (boost::shared_ptr<Region>, OverlapCache const &, boost::shared_ptr<Region>) const;
void recursive_regions_touched_sub (boost::shared_ptr<Region>, OverlapCache const &, boost::shared_ptr<Region>, RegionList &) const;
void timestamp_layer_op (LayerOp, boost::shared_ptr<Region>);
uint64_t layer_op_counter;
bool _relayer_suspended;
};
} /* namespace ARDOUR */

View File

@ -65,6 +65,7 @@ namespace Properties {
extern PBD::PropertyDescriptor<float> stretch;
extern PBD::PropertyDescriptor<float> shift;
extern PBD::PropertyDescriptor<PositionLockStyle> position_lock_style;
extern PBD::PropertyDescriptor<uint64_t> layering_index;
};
class Playlist;
@ -112,7 +113,6 @@ class Region
framepos_t start () const { return _start; }
framecnt_t length () const { return _length; }
layer_t layer () const { return _layer; }
Evoral::Range<framepos_t> bounds () const;
framecnt_t source_length(uint32_t n) const;
uint32_t max_source_level () const;
@ -202,7 +202,7 @@ class Region
void cut_front (framepos_t new_position);
void cut_end (framepos_t new_position);
void set_layer (layer_t l); /* ONLY Playlist should call this */
void set_layer (layer_t l); /* ONLY Playlist can call this */
void raise ();
void lower ();
void raise_to_top ();
@ -252,8 +252,8 @@ class Region
virtual boost::shared_ptr<Region> get_parent() const;
uint64_t last_layer_op (LayerOp) const;
void set_last_layer_op (LayerOp, uint64_t);
uint64_t layering_index () const { return _layering_index; }
void set_layering_index (uint64_t when) { _layering_index = when; }
virtual bool is_dependent() const { return false; }
virtual bool depends_on (boost::shared_ptr<Region> /*other*/) const { return false; }
@ -294,12 +294,11 @@ class Region
void invalidate_transients ();
void drop_sources ();
void set_pending_layer (double);
bool reset_pending_layer ();
boost::optional<double> pending_layer () const;
/** @return our bounds the last time our relayer() method was called */
Evoral::Range<framepos_t> last_relayer_bounds () const {
return Evoral::Range<framepos_t> (_last_relayer_bounds_from, _last_relayer_bounds_to);
}
void drop_sources ();
protected:
friend class RegionFactory;
@ -384,18 +383,15 @@ class Region
PBD::Property<float> _stretch;
PBD::Property<float> _shift;
PBD::EnumProperty<PositionLockStyle> _position_lock_style;
PBD::Property<uint64_t> _layering_index;
/* XXX: could use a Evoral::Range<> but I'm too lazy to make PBD::Property serialize such a thing nicely */
PBD::Property<framepos_t> _last_relayer_bounds_from; ///< from of our bounds last time relayer() was called
PBD::Property<framepos_t> _last_relayer_bounds_to; ///< to of our bounds last time relayer() was called
PBD::Property<uint64_t> _last_layer_op_add;
PBD::Property<uint64_t> _last_layer_op_bounds_change;
framecnt_t _last_length;
framepos_t _last_position;
mutable RegionEditState _first_edit;
Timecode::BBT_Time _bbt_time;
boost::optional<double> _pending_layer;
void register_properties ();
void use_sources (SourceList const &);

View File

@ -36,22 +36,6 @@ struct RegionSortByLayer {
}
};
struct RegionSortByAdd {
bool operator() (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
return (
(a->last_layer_op (LayerOpAdd) < b->last_layer_op (LayerOpAdd))
);
}
};
struct RegionSortByAddOrBounds {
bool operator() (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
uint64_t const p = std::max (a->last_layer_op (LayerOpAdd), a->last_layer_op (LayerOpBoundsChange));
uint64_t const q = std::max (b->last_layer_op (LayerOpAdd), b->last_layer_op (LayerOpBoundsChange));
return p < q;
}
};
} // namespace
#endif /* __libardour_region_sorters_h__ */

View File

@ -47,8 +47,6 @@ CONFIG_VARIABLE_SPECIAL(std::string, audio_search_path, "audio-search-path", "",
CONFIG_VARIABLE_SPECIAL(std::string, midi_search_path, "midi-search-path", "", search_path_expand)
CONFIG_VARIABLE (std::string, bwf_country_code, "bwf-country-code", "US")
CONFIG_VARIABLE (std::string, bwf_organization_code, "bwf-organization-code", "US")
CONFIG_VARIABLE (LayerModel, layer_model, "layer-model", AddOrBoundsChangeHigher)
CONFIG_VARIABLE (bool, relayer_on_all_edits, "relayer-on-all-edits", true)
CONFIG_VARIABLE (std::string, auditioner_output_left, "auditioner-output-left", "default")
CONFIG_VARIABLE (std::string, auditioner_output_right, "auditioner-output-right", "default")
CONFIG_VARIABLE (bool, timecode_source_is_synced, "timecode-source-is-synced", true)

View File

@ -409,12 +409,6 @@ namespace ARDOUR {
ShortCrossfade
};
enum LayerModel {
LaterHigher,
AddOrBoundsChangeHigher,
AddHigher
};
enum ListenPosition {
AfterFaderListen,
PreFaderListen
@ -592,11 +586,6 @@ namespace ARDOUR {
FadeLogB
};
enum LayerOp {
LayerOpAdd,
LayerOpBoundsChange
};
} // namespace ARDOUR
@ -613,7 +602,6 @@ std::istream& operator>>(std::istream& o, ARDOUR::PFLPosition& sf);
std::istream& operator>>(std::istream& o, ARDOUR::AFLPosition& sf);
std::istream& operator>>(std::istream& o, ARDOUR::RemoteModel& sf);
std::istream& operator>>(std::istream& o, ARDOUR::ListenPosition& sf);
std::istream& operator>>(std::istream& o, ARDOUR::LayerModel& sf);
std::istream& operator>>(std::istream& o, ARDOUR::InsertMergePolicy& sf);
std::istream& operator>>(std::istream& o, ARDOUR::CrossfadeModel& sf);
std::istream& operator>>(std::istream& o, ARDOUR::SyncSource& sf);
@ -634,7 +622,6 @@ std::ostream& operator<<(std::ostream& o, const ARDOUR::PFLPosition& sf);
std::ostream& operator<<(std::ostream& o, const ARDOUR::AFLPosition& sf);
std::ostream& operator<<(std::ostream& o, const ARDOUR::RemoteModel& sf);
std::ostream& operator<<(std::ostream& o, const ARDOUR::ListenPosition& sf);
std::ostream& operator<<(std::ostream& o, const ARDOUR::LayerModel& sf);
std::ostream& operator<<(std::ostream& o, const ARDOUR::InsertMergePolicy& sf);
std::ostream& operator<<(std::ostream& o, const ARDOUR::CrossfadeModel& sf);
std::ostream& operator<<(std::ostream& o, const ARDOUR::SyncSource& sf);

View File

@ -1510,6 +1510,8 @@ AudioDiskstream::transport_stopped_wallclock (struct tm& when, time_t twhen, boo
}
i_am_the_modifier++;
region->set_pending_layer (max_layer);
_playlist->add_region (region, (*ci)->start, 1, non_layered());
i_am_the_modifier--;

View File

@ -73,7 +73,6 @@ setup_enum_writer ()
RemoteModel _RemoteModel;
DenormalModel _DenormalModel;
CrossfadeModel _CrossfadeModel;
LayerModel _LayerModel;
InsertMergePolicy _InsertMergePolicy;
ListenPosition _ListenPosition;
SampleFormat _SampleFormat;
@ -264,11 +263,6 @@ setup_enum_writer ()
REGISTER_ENUM (ShortCrossfade);
REGISTER (_CrossfadeModel);
REGISTER_ENUM (LaterHigher);
REGISTER_ENUM (AddOrBoundsChangeHigher);
REGISTER_ENUM (AddHigher);
REGISTER (_LayerModel);
REGISTER_ENUM (InsertMergeReject);
REGISTER_ENUM (InsertMergeRelax);
REGISTER_ENUM (InsertMergeReplace);
@ -711,19 +705,6 @@ std::ostream& operator<<(std::ostream& o, const ListenPosition& var)
std::string s = enum_2_string (var);
return o << s;
}
std::istream& operator>>(std::istream& o, LayerModel& var)
{
std::string s;
o >> s;
var = (LayerModel) string_2_enum (s, var);
return o;
}
std::ostream& operator<<(std::ostream& o, const LayerModel& var)
{
std::string s = enum_2_string (var);
return o << s;
}
std::istream& operator>>(std::istream& o, InsertMergePolicy& var)
{

View File

@ -30,7 +30,6 @@
#include "pbd/convert.h"
#include "pbd/failed_constructor.h"
#include "pbd/stacktrace.h"
#include "pbd/stateful_diff_command.h"
#include "pbd/xml++.h"
@ -187,8 +186,6 @@ Playlist::Playlist (boost::shared_ptr<const Playlist> other, string namestr, boo
in_partition = false;
subcnt = 0;
_frozen = other->_frozen;
layer_op_counter = other->layer_op_counter;
}
Playlist::Playlist (boost::shared_ptr<const Playlist> other, framepos_t start, framecnt_t cnt, string str, bool hide)
@ -256,6 +253,7 @@ Playlist::Playlist (boost::shared_ptr<const Playlist> other, framepos_t start, f
plist.add (Properties::length, len);
plist.add (Properties::name, new_name);
plist.add (Properties::layer, region->layer());
plist.add (Properties::layering_index, region->layering_index());
new_region = RegionFactory::RegionFactory::create (region, plist);
@ -318,9 +316,7 @@ Playlist::init (bool hide)
in_partition = false;
subcnt = 0;
_frozen = false;
layer_op_counter = 0;
_combine_ops = 0;
_relayer_suspended = false;
_session.history().BeginUndoRedo.connect_same_thread (*this, boost::bind (&Playlist::begin_undo, this));
_session.history().EndUndoRedo.connect_same_thread (*this, boost::bind (&Playlist::end_undo, this));
@ -485,8 +481,6 @@ Playlist::notify_region_moved (boost::shared_ptr<Region> r)
{
Evoral::RangeMove<framepos_t> const move (r->last_position (), r->length (), r->position ());
/* We could timestamp the region's layer op here, but we're doing it in region_bounds_changed */
if (holding_state ()) {
pending_range_moves.push_back (move);
@ -503,8 +497,6 @@ Playlist::notify_region_moved (boost::shared_ptr<Region> r)
void
Playlist::notify_region_start_trimmed (boost::shared_ptr<Region> r)
{
timestamp_layer_op (LayerOpBoundsChange, r);
if (r->position() >= r->last_position()) {
/* trimmed shorter */
return;
@ -528,8 +520,6 @@ Playlist::notify_region_start_trimmed (boost::shared_ptr<Region> r)
void
Playlist::notify_region_end_trimmed (boost::shared_ptr<Region> r)
{
timestamp_layer_op (LayerOpBoundsChange, r);
if (r->length() < r->last_length()) {
/* trimmed shorter */
}
@ -556,8 +546,6 @@ Playlist::notify_region_added (boost::shared_ptr<Region> r)
as though it could be.
*/
timestamp_layer_op (LayerOpAdd, r);
if (holding_state()) {
pending_adds.insert (r);
pending_contents_change = true;
@ -566,7 +554,6 @@ Playlist::notify_region_added (boost::shared_ptr<Region> r)
pending_contents_change = false;
RegionAdded (boost::weak_ptr<Region> (r)); /* EMIT SIGNAL */
ContentsChanged (); /* EMIT SIGNAL */
relayer (r);
}
}
@ -584,35 +571,31 @@ Playlist::flush_notifications (bool from_undo)
in_flush = true;
/* We have:
pending_bounds: regions whose bounds position and/or length changes
pending_removes: regions which were removed
pending_adds: regions which were added
pending_length: true if the playlist length might have changed
pending_contents_change: true if there was almost any change in the playlist
pending_range_moves: details of periods of time that have been moved about (when regions have been moved)
*/
if (!pending_bounds.empty() || !pending_removes.empty() || !pending_adds.empty()) {
regions_changed = true;
}
/* Make a list of regions that need relayering */
RegionList regions_to_relayer;
/* we have no idea what order the regions ended up in pending
bounds (it could be based on selection order, for example).
so, to preserve layering in the "most recently moved is higher"
model, sort them by existing layer, then timestamp them.
*/
// RegionSortByLayer cmp;
// pending_bounds.sort (cmp);
for (RegionList::iterator r = pending_bounds.begin(); r != pending_bounds.end(); ++r) {
regions_to_relayer.push_back (*r);
dependent_checks_needed.insert (*r);
}
for (s = pending_removes.begin(); s != pending_removes.end(); ++s) {
remove_dependents (*s);
// cerr << _name << " sends RegionRemoved\n";
RegionRemoved (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
}
for (s = pending_adds.begin(); s != pending_adds.end(); ++s) {
// cerr << _name << " sends RegionAdded\n";
/* don't emit RegionAdded signal until relayering is done,
so that the region is fully setup by the time
anyone hear's that its been added
@ -621,14 +604,18 @@ Playlist::flush_notifications (bool from_undo)
}
if (regions_changed || pending_contents_change) {
if (!in_set_state) {
relayer ();
}
pending_contents_change = false;
// cerr << _name << " sends 5 contents change @ " << get_microseconds() << endl;
ContentsChanged (); /* EMIT SIGNAL */
// cerr << _name << "done contents change @ " << get_microseconds() << endl;
}
for (s = pending_adds.begin(); s != pending_adds.end(); ++s) {
(*s)->clear_changes ();
RegionAdded (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
regions_to_relayer.push_back (*s);
}
for (s = dependent_checks_needed.begin(); s != dependent_checks_needed.end(); ++s) {
@ -643,14 +630,6 @@ Playlist::flush_notifications (bool from_undo)
RegionsExtended (pending_region_extensions);
}
if (!regions_to_relayer.empty () && !from_undo) {
relayer (regions_to_relayer);
}
if (pending_layering) {
LayeringChanged (); /* EMIT SIGNAL */
}
clear_pending ();
in_flush = false;
@ -686,6 +665,7 @@ Playlist::flush_notifications (bool from_undo)
}
if (itimes >= 1) {
region->set_pending_layer (DBL_MAX);
add_region_internal (region, pos);
pos += region->length();
--itimes;
@ -698,6 +678,7 @@ Playlist::flush_notifications (bool from_undo)
for (int i = 0; i < itimes; ++i) {
boost::shared_ptr<Region> copy = RegionFactory::create (region, true);
copy->set_pending_layer (DBL_MAX);
add_region_internal (copy, pos);
pos += region->length();
}
@ -718,6 +699,7 @@ Playlist::flush_notifications (bool from_undo)
plist.add (Properties::layer, region->layer());
boost::shared_ptr<Region> sub = RegionFactory::create (region, plist);
sub->set_pending_layer (DBL_MAX);
add_region_internal (sub, pos);
}
}
@ -758,6 +740,11 @@ Playlist::flush_notifications (bool from_undo)
possibly_splice_unlocked (position, region->length(), region);
if (!holding_state ()) {
/* layers get assigned from XML state, and are not reset during undo/redo */
relayer ();
}
/* we need to notify the existence of new region before checking dependents. Ick. */
notify_region_added (region);
@ -780,6 +767,7 @@ Playlist::flush_notifications (bool from_undo)
_splicing = true;
remove_region_internal (old);
newr->set_pending_layer (newr->layer ());
add_region_internal (newr, pos);
_splicing = old_sp;
@ -817,6 +805,7 @@ Playlist::flush_notifications (bool from_undo)
possibly_splice_unlocked (pos, -distance);
if (!holding_state ()) {
relayer ();
remove_dependents (region);
}
@ -873,16 +862,12 @@ Playlist::flush_notifications (bool from_undo)
* start and end if cutting == true. Regions that lie entirely within start and end are always
* removed.
*/
void
Playlist::partition_internal (framepos_t start, framepos_t end, bool cutting, RegionList& thawlist)
{
RegionList new_regions;
/* Don't relayer regions that are created during this operation; leave them
on the same region as the original.
*/
suspend_relayer ();
{
RegionLock rlock (this);
@ -961,7 +946,8 @@ Playlist::flush_notifications (bool from_undo)
plist.add (Properties::start, current->start() + (pos2 - pos1));
plist.add (Properties::length, pos3 - pos2);
plist.add (Properties::name, new_name);
plist.add (Properties::layer, current->layer());
plist.add (Properties::layer, current->layer ());
plist.add (Properties::layering_index, current->layering_index ());
plist.add (Properties::automatic, true);
plist.add (Properties::left_of_split, true);
plist.add (Properties::right_of_split, true);
@ -980,7 +966,8 @@ Playlist::flush_notifications (bool from_undo)
plist.add (Properties::start, current->start() + (pos3 - pos1));
plist.add (Properties::length, pos4 - pos3);
plist.add (Properties::name, new_name);
plist.add (Properties::layer, current->layer());
plist.add (Properties::layer, current->layer ());
plist.add (Properties::layering_index, current->layering_index ());
plist.add (Properties::automatic, true);
plist.add (Properties::right_of_split, true);
@ -1018,7 +1005,8 @@ Playlist::flush_notifications (bool from_undo)
plist.add (Properties::start, current->start() + (pos2 - pos1));
plist.add (Properties::length, pos4 - pos2);
plist.add (Properties::name, new_name);
plist.add (Properties::layer, current->layer());
plist.add (Properties::layer, current->layer ());
plist.add (Properties::layering_index, current->layering_index ());
plist.add (Properties::automatic, true);
plist.add (Properties::left_of_split, true);
@ -1061,7 +1049,8 @@ Playlist::flush_notifications (bool from_undo)
plist.add (Properties::start, current->start());
plist.add (Properties::length, pos3 - pos1);
plist.add (Properties::name, new_name);
plist.add (Properties::layer, current->layer());
plist.add (Properties::layer, current->layer ());
plist.add (Properties::layering_index, current->layering_index ());
plist.add (Properties::automatic, true);
plist.add (Properties::right_of_split, true);
@ -1108,8 +1097,6 @@ Playlist::flush_notifications (bool from_undo)
for (RegionList::iterator i = new_regions.begin(); i != new_regions.end(); ++i) {
check_dependents (*i, false);
}
resume_relayer ();
}
boost::shared_ptr<Playlist>
@ -1210,7 +1197,7 @@ Playlist::flush_notifications (bool from_undo)
int itimes = (int) floor (times);
framepos_t pos = position;
framecnt_t const shift = other->_get_extent().second;
layer_t top_layer = regions.size();
layer_t top = top_layer ();
while (itimes--) {
for (RegionList::iterator i = other->regions.begin(); i != other->regions.end(); ++i) {
@ -1220,7 +1207,7 @@ Playlist::flush_notifications (bool from_undo)
the ordering they had in the original playlist.
*/
copy_of_region->set_layer (copy_of_region->layer() + top_layer);
copy_of_region->set_pending_layer (copy_of_region->layer() + top);
add_region_internal (copy_of_region, (*i)->position() + pos);
}
pos += shift;
@ -1242,6 +1229,7 @@ Playlist::flush_notifications (bool from_undo)
while (itimes--) {
boost::shared_ptr<Region> copy = RegionFactory::create (region, true);
copy->set_pending_layer (DBL_MAX);
add_region_internal (copy, pos);
pos += region->length();
}
@ -1259,6 +1247,7 @@ Playlist::flush_notifications (bool from_undo)
plist.add (Properties::name, name);
boost::shared_ptr<Region> sub = RegionFactory::create (region, plist);
sub->set_pending_layer (DBL_MAX);
add_region_internal (sub, pos);
}
}
@ -1360,6 +1349,8 @@ Playlist::flush_notifications (bool from_undo)
plist.add (Properties::length, before);
plist.add (Properties::name, before_name);
plist.add (Properties::left_of_split, true);
plist.add (Properties::layering_index, region->layering_index ());
plist.add (Properties::layer, region->layer ());
/* note: we must use the version of ::create with an offset here,
since it supplies that offset to the Region constructor, which
@ -1377,6 +1368,8 @@ Playlist::flush_notifications (bool from_undo)
plist.add (Properties::length, after);
plist.add (Properties::name, after_name);
plist.add (Properties::right_of_split, true);
plist.add (Properties::layering_index, region->layering_index ());
plist.add (Properties::layer, region->layer ());
/* same note as above */
right = RegionFactory::create (region, before, plist);
@ -1470,8 +1463,6 @@ Playlist::flush_notifications (bool from_undo)
if (what_changed.contains (Properties::position)) {
timestamp_layer_op (LayerOpBoundsChange, region);
/* remove it from the list then add it back in
the right place again.
*/
@ -1512,7 +1503,7 @@ Playlist::flush_notifications (bool from_undo)
pending_bounds.push_back (region);
} else {
notify_contents_changed ();
relayer (region);
relayer ();
check_dependents (region, false);
}
}
@ -1572,9 +1563,9 @@ Playlist::flush_notifications (bool from_undo)
notify_region_start_trimmed (region);
}
if (what_changed.contains (Properties::layer)) {
notify_layering_changed ();
}
/* don't notify about layer changes, since we are the only object that can initiate
them, and we notify in ::relayer()
*/
if (what_changed.contains (our_interests)) {
save = true;
@ -2134,7 +2125,6 @@ Playlist::flush_notifications (bool from_undo)
return -1;
}
suspend_relayer ();
freeze ();
plist = node.properties();
@ -2196,7 +2186,7 @@ Playlist::flush_notifications (bool from_undo)
}
add_region (region, region->position(), 1.0);
region->resume_property_changes ();
}
@ -2217,7 +2207,6 @@ Playlist::flush_notifications (bool from_undo)
thaw ();
notify_contents_changed ();
resume_relayer ();
in_set_state--;
first_set_state = false;
@ -2345,277 +2334,189 @@ Playlist::set_edit_mode (EditMode mode)
_edit_mode = mode;
}
/** Relayer a region. See the other relayer() methods for commentary. */
struct RelayerSort {
bool operator () (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
return a->layering_index() < b->layering_index();
}
};
void
Playlist::relayer (boost::shared_ptr<Region> region)
Playlist::relayer ()
{
if (_relayer_suspended) {
/* never compute layers when changing state for undo/redo or setting from XML */
if (in_update || in_set_state) {
return;
}
RegionList r;
r.push_back (region);
relayer (r);
}
Playlist::TemporaryLayers
Playlist::compute_temporary_layers (RegionList const & relayer_regions)
{
TemporaryLayers temporary_layers;
OverlapCache cache (this);
bool changed = false;
for (RegionList::const_iterator i = relayer_regions.begin(); i != relayer_regions.end(); ++i) {
/* Build up a new list of regions on each layer, stored in a set of lists
each of which represent some period of time on some layer. The idea
is to avoid having to search the entire region list to establish whether
each region overlaps another */
DEBUG_TRACE (DEBUG::Layering, string_compose ("Compute temporary layer for %1\n", (*i)->name()));
/* current_overlaps: regions that overlap *i now */
RegionList current_overlaps = cache.get ((*i)->bounds ());
current_overlaps.remove (*i);
/* how many pieces to divide this playlist's time up into */
int const divisions = 512;
DEBUG_TRACE (DEBUG::Layering, "Current overlaps:\n");
for (RegionList::iterator j = current_overlaps.begin(); j != current_overlaps.end(); ++j) {
DEBUG_TRACE (DEBUG::Layering, string_compose ("\t%1\n", (*j)->name()));
}
/* find the start and end positions of the regions on this playlist */
framepos_t start = INT64_MAX;
framepos_t end = 0;
for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
start = min (start, (*i)->position());
end = max (end, (*i)->position() + (*i)->length());
}
/* hence the size of each time division */
double const division_size = (end - start) / double (divisions);
vector<vector<RegionList> > layers;
layers.push_back (vector<RegionList> (divisions));
RegionList copy = regions.rlist();
RegionList pending;
/* Remove regions with pending relayers */
for (RegionList::iterator i = copy.begin(); i != copy.end(); ) {
RegionList::iterator j = i;
++j;
/* overlaps_to_preserve: regions that overlap *i now, but which aren't being
worked on during this relayer: these will have their relationship with
*i preserved.
*/
RegionList overlaps_to_preserve;
if ((*i)->pending_layer()) {
pending.push_back (*i);
copy.erase (i);
}
/* overlaps_to_check: regions that overlap *i now, and must be checked to
see if *i is still on the correct layer with respect to them (according
to current layering rules).
*/
RegionList overlaps_to_check;
i = j;
}
if (_session.config.get_relayer_on_all_edits () || (*i)->last_layer_op (LayerOpAdd) > (*i)->last_layer_op (LayerOpBoundsChange)) {
/* We're configured to relayer on all edits, or this region has had
no edit since it was added to the playlist, so we're relayering
the whole lot; in this case there are no `overlaps_to_preserve'.
*/
overlaps_to_check = current_overlaps;
} else {
/* We're only relayering new overlaps; find them */
RegionList last_overlaps = cache.get ((*i)->last_relayer_bounds ());
last_overlaps.remove (*i);
for (RegionList::const_iterator j = current_overlaps.begin(); j != current_overlaps.end(); ++j) {
if (find (last_overlaps.begin(), last_overlaps.end(), *j) == last_overlaps.end()) {
/* This is a new overlap, which must be checked */
overlaps_to_check.push_back (*j);
} else {
/* This is an existing overlap, which must be preserved */
overlaps_to_preserve.push_back (*j);
}
/* Sort the remainder */
copy.sort (RelayerSort ());
/* Re-insert the pending layers in the right places */
for (RegionList::iterator i = pending.begin(); i != pending.end(); ++i) {
RegionList::iterator j = copy.begin();
while (j != copy.end ()) {
if ((*j)->pending_layer().get_value_or ((*j)->layer ()) > (*i)->pending_layer().get ()) {
break;
}
}
if (overlaps_to_check.empty ()) {
/* There are no overlaps to check, so just leave everything as it is */
continue;
}
DEBUG_TRACE (DEBUG::Layering, "Overlaps to check:\n");
for (RegionList::iterator j = overlaps_to_check.begin(); j != overlaps_to_check.end(); ++j) {
DEBUG_TRACE (DEBUG::Layering, string_compose ("\t%1\n", (*j)->name()));
}
/* Put *i on our overlaps_to_check_list */
overlaps_to_check.push_back (*i);
/* And sort it according to the current layer model */
switch (_session.config.get_layer_model()) {
case LaterHigher:
overlaps_to_check.sort (RegionSortByPosition ());
break;
case AddOrBoundsChangeHigher:
overlaps_to_check.sort (RegionSortByAddOrBounds ());
break;
case AddHigher:
overlaps_to_check.sort (RegionSortByAdd ());
break;
}
/* Now find *i in our overlaps_to_check list; within this list it will be in the
right place wrt the current layering rules, so we can work out the layers of the
nearest regions below and above.
*/
double previous_layer = -DBL_MAX;
double next_layer = DBL_MAX;
RegionList::const_iterator j = overlaps_to_check.begin();
while (*j != *i) {
previous_layer = temporary_layers.get (*j);
++j;
}
copy.insert (j, *i);
}
/* we must have found *i */
assert (j != overlaps_to_check.end ());
bool had_pending = false;
++j;
if (j != overlaps_to_check.end ()) {
next_layer = temporary_layers.get (*j);
for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
/* reset the pending layer for every region now that we're relayering */
if ((*i)->reset_pending_layer ()) {
had_pending = true;
}
if (next_layer < previous_layer) {
/* If this happens, it means that it's impossible to put *i between overlaps_to_check
in a way that satisfies the current layering rule. So we'll punt and put *i
above previous_layer.
*/
next_layer = DBL_MAX;
}
/* Now we know where *i and overlaps_to_preserve should go: between previous_layer and
next_layer.
/* find the time divisions that this region covers; if there are no regions on the list,
division_size will equal 0 and in this case we'll just say that
start_division = end_division = 0.
*/
int start_division = 0;
int end_division = 0;
DEBUG_TRACE (DEBUG::Layering, string_compose ("%1 and deps need to go between %2 and %3\n", (*i)->name(), previous_layer, next_layer));
if (division_size > 0) {
start_division = floor ( ((*i)->position() - start) / division_size);
end_division = floor ( ((*i)->position() + (*i)->length() - start) / division_size );
if (end_division == divisions) {
end_division--;
}
}
/* Recurse into overlaps_to_preserve to find dependents */
RegionList recursed_overlaps_to_preserve;
for (RegionList::const_iterator k = overlaps_to_preserve.begin(); k != overlaps_to_preserve.end(); ++k) {
recursed_overlaps_to_preserve.push_back (*k);
RegionList touched = recursive_regions_touched (*k, cache, *i);
for (RegionList::iterator m = touched.begin(); m != touched.end(); ++m) {
if (find (recursed_overlaps_to_preserve.begin(), recursed_overlaps_to_preserve.end(), *m) == recursed_overlaps_to_preserve.end()) {
recursed_overlaps_to_preserve.push_back (*m);
assert (divisions == 0 || end_division < divisions);
/* find the lowest layer that this region can go on */
size_t j = layers.size();
while (j > 0) {
/* try layer j - 1; it can go on if it overlaps no other region
that is already on that layer
*/
bool overlap = false;
for (int k = start_division; k <= end_division; ++k) {
RegionList::iterator l = layers[j-1][k].begin ();
while (l != layers[j-1][k].end()) {
if ((*l)->overlap_equivalent (*i)) {
overlap = true;
break;
}
l++;
}
if (overlap) {
break;
}
}
}
/* Put *i into the overlaps_to_preserve list */
recursed_overlaps_to_preserve.push_back (*i);
/* Sort it by layer, so that we preserve layering */
recursed_overlaps_to_preserve.sort (SortByTemporaryLayer (temporary_layers));
/* Divide available space up into chunks so that we can relayer everything in that space */
double const space = (next_layer - previous_layer) / (recursed_overlaps_to_preserve.size() + 1);
/* And relayer */
int m = 1;
for (RegionList::const_iterator k = recursed_overlaps_to_preserve.begin(); k != recursed_overlaps_to_preserve.end(); ++k) {
temporary_layers.set (*k, previous_layer + space * m);
++m;
}
}
return temporary_layers;
}
/** Take a list of temporary layer indices and set up the layers of all regions
* based on them.
*/
void
Playlist::commit_temporary_layers (TemporaryLayers const & temporary_layers)
{
/* Sort all the playlist's regions by layer, ascending */
RegionList all_regions = regions.rlist ();
all_regions.sort (SortByTemporaryLayer (temporary_layers));
DEBUG_TRACE (DEBUG::Layering, "Commit layering:\n");
for (RegionList::iterator i = all_regions.begin(); i != all_regions.end(); ++i) {
/* Go through the regions that we have already layered and hence work
out the maximum layer index that is in used at some point during
region *i.
*/
layer_t max_layer_here = 0;
bool have_overlap = false;
for (RegionList::iterator j = all_regions.begin(); j != i; ++j) {
if ((*j)->overlap_equivalent (*i)) {
max_layer_here = max ((*j)->layer (), max_layer_here);
have_overlap = true;
if (overlap) {
/* overlap, so we must use layer j */
break;
}
--j;
}
if (have_overlap) {
/* *i overlaps something, so put it on the next available layer */
(*i)->set_layer (max_layer_here + 1);
} else {
/* no overlap, so put on the bottom layer */
(*i)->set_layer (0);
if (j == layers.size()) {
/* we need a new layer for this region */
layers.push_back (vector<RegionList> (divisions));
}
DEBUG_TRACE (DEBUG::Layering, string_compose ("\t%1 temporary %2 committed %3\n", (*i)->name(), temporary_layers.get (*i), (*i)->layer()));
}
}
/** Relayer a list of regions.
*
* Taking each region R in turn, this method examines the regions O that overlap R in time.
* If the session configuration option "relayer-on-all-moves" is false, we reduce O so that
* it contains only those regions with which new overlaps have been formed since the last
* relayer.
*
* We then change the layer of R and its indirect overlaps so that R meets the current
* Session layer model with respect to O. See doc/layering.
*/
/* put a reference to this region in each of the divisions that it exists in */
for (int k = start_division; k <= end_division; ++k) {
layers[j][k].push_back (*i);
}
void
Playlist::relayer (RegionList const & relayer_regions)
{
if (_relayer_suspended) {
return;
if ((*i)->layer() != j) {
changed = true;
}
(*i)->set_layer (j);
}
/* We do this in two parts: first; compute `temporary layer' indices for
regions on the playlist. These are (possibly) fractional indices, which
are a convenient means of working with things when you want to insert layers
between others.
*/
TemporaryLayers temporary_layers = compute_temporary_layers (relayer_regions);
/* Second, we fix up these temporary layers and `commit' them by writing
them to the regions involved.
*/
commit_temporary_layers (temporary_layers);
}
/** Put a region on some fractional layer and sort everything else out around it.
* This can be used to force a region into some layering; for example, calling
* this method with temporary_layer == -0.5 will put the region at the bottom of
* the stack.
*/
void
Playlist::relayer (boost::shared_ptr<Region> region, double temporary_layer)
{
if (_relayer_suspended) {
return;
if (changed) {
notify_layering_changed ();
}
TemporaryLayers t;
t.set (region, temporary_layer);
commit_temporary_layers (t);
if (had_pending) {
uint64_t i = 0;
for (RegionList::iterator j = copy.begin(); j != copy.end(); ++j) {
(*j)->set_layering_index (i++);
}
}
}
void
Playlist::raise_region (boost::shared_ptr<Region> region)
{
relayer (region, region->layer() + 1.5);
region->set_pending_layer (region->layer() + 1.5);
relayer ();
}
void
Playlist::lower_region (boost::shared_ptr<Region> region)
{
relayer (region, region->layer() - 1.5);
region->set_pending_layer (region->layer() - 1.5);
relayer ();
}
void
Playlist::raise_region_to_top (boost::shared_ptr<Region> region)
{
relayer (region, max_layer);
region->set_pending_layer (DBL_MAX);
relayer ();
}
void
Playlist::lower_region_to_bottom (boost::shared_ptr<Region> region)
{
relayer (region, -0.5);
region->set_pending_layer (-0.5);
relayer ();
}
void
@ -2750,24 +2651,9 @@ Playlist::set_frozen (bool yn)
_frozen = yn;
}
void
Playlist::timestamp_layer_op (LayerOp op, boost::shared_ptr<Region> region)
{
region->set_last_layer_op (op, ++layer_op_counter);
}
/** Find the next or previous region after `region' (next if dir > 0, previous otherwise)
* and swap its position with `region'.
*/
void
Playlist::shuffle (boost::shared_ptr<Region> region, int dir)
{
/* As regards layering, the calls we make to set_position() will
perform layering as if the regions had been moved, which I think
is about right.
*/
bool moved = false;
if (region->locked()) {
@ -2872,9 +2758,13 @@ Playlist::shuffle (boost::shared_ptr<Region> region, int dir)
_shuffling = false;
if (moved) {
relayer ();
check_dependents (region, false);
notify_contents_changed();
}
}
bool
@ -3293,145 +3183,16 @@ Playlist::set_orig_track_id (const PBD::ID& id)
_orig_track_id = id;
}
/** Set the temporary layer for a region */
void
Playlist::TemporaryLayers::set (boost::shared_ptr<Region> r, double l)
uint64_t
Playlist::highest_layering_index () const
{
_map[r] = l;
}
RegionLock rlock (const_cast<Playlist *> (this));
/** Return the temporary layer for a region, if one has been specified
* to this TemporaryLayers object; if not return the region's current
* layer.
*/
double
Playlist::TemporaryLayers::get (boost::shared_ptr<Region> r) const
{
Map::const_iterator i = _map.find (r);
if (i != _map.end ()) {
return i->second;
uint64_t h = 0;
for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
h = max (h, (*i)->layering_index ());
}
return double (r->layer ());
}
int const Playlist::OverlapCache::_divisions = 512;
/** Set up an OverlapCache for a playlist; the cache will only be valid until
* the Playlist is changed.
*/
Playlist::OverlapCache::OverlapCache (Playlist* playlist)
: _range (0, 0)
{
/* Find the start and end positions of the regions on this playlist */
_range = Evoral::Range<framepos_t> (max_framepos, 0);
RegionList const & rl = playlist->region_list().rlist ();
for (RegionList::const_iterator i = rl.begin(); i != rl.end(); ++i) {
Evoral::Range<framepos_t> const b = (*i)->bounds ();
_range.from = min (_range.from, b.from);
_range.to = max (_range.to, b.to);
}
/* Hence the size of each time divison */
_division_size = (_range.to - _range.from) / double (_divisions);
_cache.resize (_divisions);
/* Build the cache */
for (RegionList::const_iterator i = rl.begin(); i != rl.end(); ++i) {
pair<int, int> ind = cache_indices ((*i)->bounds ());
for (int j = ind.first; j < ind.second; ++j) {
_cache[j].push_back (*i);
}
}
}
/** @param range Range, in frames.
* @return From and to cache indices for (to is exclusive).
*/
pair<int, int>
Playlist::OverlapCache::cache_indices (Evoral::Range<framepos_t> range) const
{
range.from = max (range.from, _range.from);
range.to = min (range.to, _range.to);
pair<int, int> const p = make_pair (
floor ((range.from - _range.from) / _division_size),
ceil ((range.to - _range.from) / _division_size)
);
assert (p.first >= 0);
assert (p.second <= _divisions);
return p;
}
/** Return the regions that overlap a given range. The returned list
* is not guaranteed to be in the same order as the Playlist that it was
* generated from.
*/
Playlist::RegionList
Playlist::OverlapCache::get (Evoral::Range<framepos_t> range) const
{
if (_range.from == max_framepos) {
return RegionList ();
}
RegionList r;
pair<int, int> ind = cache_indices (range);
for (int i = ind.first; i < ind.second; ++i) {
for (RegionList::const_iterator j = _cache[i].begin(); j != _cache[i].end(); ++j) {
if ((*j)->coverage (range.from, range.to) != OverlapNone) {
r.push_back (*j);
}
}
}
r.sort ();
r.unique ();
return r;
}
void
Playlist::suspend_relayer ()
{
_relayer_suspended = true;
}
void
Playlist::resume_relayer ()
{
_relayer_suspended = false;
}
/** Examine a region and return regions which overlap it, and also those which overlap those which overlap etc.
* @param ignore Optional region which should be treated as if it doesn't exist (ie not returned in the list,
* and not recursed into).
*/
Playlist::RegionList
Playlist::recursive_regions_touched (boost::shared_ptr<Region> region, OverlapCache const & cache, boost::shared_ptr<Region> ignore) const
{
RegionList touched;
recursive_regions_touched_sub (region, cache, ignore, touched);
touched.remove (region);
return touched;
}
/** Recursive sub-routine of recursive_regions_touched */
void
Playlist::recursive_regions_touched_sub (
boost::shared_ptr<Region> region, OverlapCache const & cache, boost::shared_ptr<Region> ignore, RegionList & touched
) const
{
RegionList r = cache.get (region->bounds ());
for (RegionList::iterator i = r.begin(); i != r.end(); ++i) {
if (find (touched.begin(), touched.end(), *i) == touched.end() && *i != ignore) {
touched.push_back (*i);
recursive_regions_touched_sub (*i, cache, ignore, touched);
}
}
return h;
}

View File

@ -73,10 +73,7 @@ namespace ARDOUR {
PBD::PropertyDescriptor<float> stretch;
PBD::PropertyDescriptor<float> shift;
PBD::PropertyDescriptor<PositionLockStyle> position_lock_style;
PBD::PropertyDescriptor<framepos_t> last_relayer_bounds_from;
PBD::PropertyDescriptor<framepos_t> last_relayer_bounds_to;
PBD::PropertyDescriptor<uint64_t> last_layer_op_add;
PBD::PropertyDescriptor<uint64_t> last_layer_op_bounds_change;
PBD::PropertyDescriptor<uint64_t> layering_index;
}
}
@ -131,14 +128,8 @@ Region::make_property_quarks ()
DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for shift = %1\n", Properties::shift.property_id));
Properties::position_lock_style.property_id = g_quark_from_static_string (X_("positional-lock-style"));
DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for position_lock_style = %1\n", Properties::position_lock_style.property_id));
Properties::last_relayer_bounds_from.property_id = g_quark_from_static_string (X_("last-relayer-bounds-from"));
DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for last_relayer_bounds_from = %1\n", Properties::last_relayer_bounds_from.property_id));
Properties::last_relayer_bounds_to.property_id = g_quark_from_static_string (X_("last-relayer-bounds-to"));
DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for last_relayer_bounds_to = %1\n", Properties::last_relayer_bounds_to.property_id));
Properties::last_layer_op_add.property_id = g_quark_from_static_string (X_("last-layer-op-add"));
DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for last_layer_op_add = %1\n", Properties::last_layer_op_add.property_id));
Properties::last_layer_op_bounds_change.property_id = g_quark_from_static_string (X_("last-layer-op-bounds-change"));
DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for last_layer_op_bounds_change = %1\n", Properties::last_layer_op_bounds_change.property_id));
Properties::layering_index.property_id = g_quark_from_static_string (X_("layering-index"));
DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for layering_index = %1\n", Properties::layering_index.property_id));
}
void
@ -169,10 +160,7 @@ Region::register_properties ()
add_property (_stretch);
add_property (_shift);
add_property (_position_lock_style);
add_property (_last_relayer_bounds_from);
add_property (_last_relayer_bounds_to);
add_property (_last_layer_op_add);
add_property (_last_layer_op_bounds_change);
add_property (_layering_index);
}
#define REGION_DEFAULT_STATE(s,l) \
@ -199,10 +187,7 @@ Region::register_properties ()
, _stretch (Properties::stretch, 1.0) \
, _shift (Properties::shift, 1.0) \
, _position_lock_style (Properties::position_lock_style, _type == DataType::AUDIO ? AudioTime : MusicTime) \
, _last_relayer_bounds_from (Properties::last_relayer_bounds_from, 0) \
, _last_relayer_bounds_to (Properties::last_relayer_bounds_to, 0) \
, _last_layer_op_add (Properties::last_layer_op_add, 0) \
, _last_layer_op_bounds_change (Properties::last_layer_op_bounds_change, 0)
, _layering_index (Properties::layering_index, 0)
#define REGION_COPY_STATE(other) \
_sync_marked (Properties::sync_marked, other->_sync_marked) \
@ -228,10 +213,7 @@ Region::register_properties ()
, _stretch (Properties::stretch, other->_stretch) \
, _shift (Properties::shift, other->_shift) \
, _position_lock_style (Properties::position_lock_style, other->_position_lock_style) \
, _last_relayer_bounds_from (Properties::last_relayer_bounds_from, other->_last_relayer_bounds_from) \
, _last_relayer_bounds_to (Properties::last_relayer_bounds_to, other->_last_relayer_bounds_to) \
, _last_layer_op_add (Properties::last_layer_op_add, other->_last_layer_op_add) \
, _last_layer_op_bounds_change (Properties::last_layer_op_bounds_change, other->_last_layer_op_bounds_change)
, _layering_index (Properties::layering_index, other->_layering_index)
/* derived-from-derived constructor (no sources in constructor) */
Region::Region (Session& s, framepos_t start, framecnt_t length, const string& name, DataType type)
@ -243,6 +225,7 @@ Region::Region (Session& s, framepos_t start, framecnt_t length, const string& n
, _first_edit (EditChangesNothing)
{
register_properties ();
/* no sources at this point */
}
@ -1134,12 +1117,9 @@ Region::set_layer (layer_t l)
{
if (_layer != l) {
_layer = l;
send_change (Properties::layer);
}
Evoral::Range<framepos_t> const b = bounds ();
_last_relayer_bounds_from = b.from;
_last_relayer_bounds_to = b.to;
}
XMLNode&
@ -1330,19 +1310,6 @@ Region::send_change (const PropertyChange& what_changed)
}
}
void
Region::set_last_layer_op (LayerOp op, uint64_t when)
{
switch (op) {
case LayerOpAdd:
_last_layer_op_add = when;
break;
case LayerOpBoundsChange:
_last_layer_op_bounds_change = when;
break;
}
}
bool
Region::overlap_equivalent (boost::shared_ptr<const Region> other) const
{
@ -1683,24 +1650,22 @@ Region::post_set (const PropertyChange& pc)
}
}
uint64_t
Region::last_layer_op (LayerOp op) const
void
Region::set_pending_layer (double l)
{
switch (op) {
case LayerOpAdd:
return _last_layer_op_add;
case LayerOpBoundsChange:
return _last_layer_op_bounds_change;
}
/* NOTREACHED */
return 0;
_pending_layer = l;
}
Evoral::Range<framepos_t>
Region::bounds () const
bool
Region::reset_pending_layer ()
{
return Evoral::Range<framepos_t> (_position, _position + _length);
bool const had = _pending_layer;
_pending_layer = boost::optional<double> ();
return had;
}
boost::optional<double>
Region::pending_layer () const
{
return _pending_layer;
}

View File

@ -19,8 +19,6 @@ export ARDOUR_PANNER_PATH=$libs/panners/2in2out:$libs/panners/1in2out:$libs/pann
if [ "$1" == "--debug" ]; then
gdb ./libs/ardour/run-tests
elif [ "$1" == "--valgrind" ]; then
valgrind --tool="memcheck" ./libs/ardour/run-tests
else
./libs/ardour/run-tests
fi

View File

@ -974,27 +974,7 @@ int
Session::load_options (const XMLNode& node)
{
LocaleGuard lg (X_("POSIX"));
/* Copy the node */
XMLNode node_copy = node;
/* XXX: evil hack: fix up sessions from before the layering alterations
(during A3 beta)
*/
XMLNodeList children = node_copy.children ();
for (XMLNodeIterator i = children.begin(); i != children.end(); ++i) {
XMLProperty* p = (*i)->property (X_("name"));
if (p && p->name() == X_("name") && p->value() == X_("layer-model") ) {
p = (*i)->property (X_("value"));
if (p && p->value() == X_("MoveAddHigher")) {
(*i)->add_property (X_("value"), X_("AddOrBoundsChangeHigher"));
}
}
}
config.set_variables (node_copy);
config.set_variables (node);
return 0;
}

View File

@ -1,14 +1,13 @@
#include "pbd/compose.h"
#include "midi++/manager.h"
#include "pbd/textreceiver.h"
#include "pbd/compose.h"
#include "ardour/session.h"
#include "ardour/audioengine.h"
#include "ardour/playlist_factory.h"
#include "ardour/source_factory.h"
#include "ardour/region.h"
#include "ardour/region_factory.h"
#include "ardour/session.h"
#include "ardour/audiosource.h"
#include "ardour/audioengine.h"
#include "playlist_layering_test.h"
#include "test_receiver.h"
CPPUNIT_TEST_SUITE_REGISTRATION (PlaylistLayeringTest);
@ -16,28 +15,68 @@ using namespace std;
using namespace ARDOUR;
using namespace PBD;
int const PlaylistLayeringTest::num_regions = 6;
class TestReceiver : public Receiver
{
protected:
void receive (Transmitter::Channel chn, const char * str) {
const char *prefix = "";
switch (chn) {
case Transmitter::Error:
prefix = ": [ERROR]: ";
break;
case Transmitter::Info:
/* ignore */
return;
case Transmitter::Warning:
prefix = ": [WARNING]: ";
break;
case Transmitter::Fatal:
prefix = ": [FATAL]: ";
break;
case Transmitter::Throw:
/* this isn't supposed to happen */
abort ();
}
/* note: iostreams are already thread-safe: no external
lock required.
*/
cout << prefix << str << endl;
if (chn == Transmitter::Fatal) {
exit (9);
}
}
};
TestReceiver test_receiver;
void
PlaylistLayeringTest::setUp ()
{
TestNeedingSession::setUp ();
string const test_wav_path = "libs/ardour/test/test.wav";
string const test_session_path = "libs/ardour/test/playlist_layering_test";
string const test_wav_path = "libs/ardour/test/playlist_layering_test/playlist_layering_test.wav";
system (string_compose ("rm -rf %1", test_session_path).c_str());
init (false, true);
SessionEvent::create_per_thread_pool ("test", 512);
test_receiver.listen_to (error);
test_receiver.listen_to (info);
test_receiver.listen_to (fatal);
test_receiver.listen_to (warning);
AudioEngine* engine = new AudioEngine ("test", "");
MIDI::Manager::create (engine->jack ());
CPPUNIT_ASSERT (engine->start () == 0);
_session = new Session (*engine, test_session_path, "playlist_layering_test");
engine->set_session (_session);
_playlist = PlaylistFactory::create (DataType::AUDIO, *_session, "test");
_source = SourceFactory::createWritable (DataType::AUDIO, *_session, test_wav_path, "", false, 44100);
system ("pwd");
/* Must write some data to our source, otherwise regions which use it will
be limited in whether they can be trimmed or not.
*/
boost::shared_ptr<AudioSource> a = boost::dynamic_pointer_cast<AudioSource> (_source);
Sample silence[512];
memset (silence, 0, 512 * sizeof (Sample));
a->write (silence, 512);
_region = new boost::shared_ptr<Region>[num_regions];
}
void
@ -45,303 +84,45 @@ PlaylistLayeringTest::tearDown ()
{
_playlist.reset ();
_source.reset ();
for (int i = 0; i < num_regions; ++i) {
for (int i = 0; i < 16; ++i) {
_region[i].reset ();
}
delete[] _region;
TestNeedingSession::tearDown ();
AudioEngine::instance()->remove_session ();
delete _session;
EnumWriter::destroy ();
MIDI::Manager::destroy ();
AudioEngine::destroy ();
}
void
PlaylistLayeringTest::create_short_regions ()
PlaylistLayeringTest::create_three_short_regions ()
{
PropertyList plist;
plist.add (Properties::start, 0);
plist.add (Properties::length, 100);
for (int i = 0; i < num_regions; ++i) {
for (int i = 0; i < 3; ++i) {
_region[i] = RegionFactory::create (_source, plist);
_region[i]->set_name (string_compose ("%1", char (int ('A') + i)));
}
}
void
PlaylistLayeringTest::laterHigher_relayerOnAll_Test ()
PlaylistLayeringTest::basicsTest ()
{
_session->config.set_layer_model (LaterHigher);
_session->config.set_relayer_on_all_edits (true);
create_short_regions ();
create_three_short_regions ();
/* three overlapping regions */
_playlist->add_region (_region[A], 0);
_playlist->add_region (_region[B], 10);
_playlist->add_region (_region[C], 20);
/* and another non-overlapping one */
_playlist->add_region (_region[D], 200);
_playlist->add_region (_region[0], 0);
_playlist->add_region (_region[1], 10);
_playlist->add_region (_region[2], 20);
/* LaterHigher means that they should be arranged thus */
CPPUNIT_ASSERT_EQUAL (layer_t (0), _region[A]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (1), _region[B]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (2), _region[C]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (0), _region[D]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (0), _region[0]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (1), _region[1]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (2), _region[2]->layer ());
_region[A]->set_position (5);
_region[0]->set_position (5);
/* Region move should have no effect in LaterHigher mode */
CPPUNIT_ASSERT_EQUAL (layer_t (0), _region[A]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (1), _region[B]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (2), _region[C]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (0), _region[D]->layer ());
/* C -> bottom should give C A B, not touching D */
_region[C]->lower_to_bottom ();
CPPUNIT_ASSERT_EQUAL (layer_t (0), _region[C]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (1), _region[A]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (2), _region[B]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (0), _region[D]->layer ());
/* C -> top should go back to A B C, not touching D */
_region[C]->raise_to_top ();
CPPUNIT_ASSERT_EQUAL (layer_t (0), _region[A]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (1), _region[B]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (2), _region[C]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (0), _region[D]->layer ());
}
void
PlaylistLayeringTest::addHigher_relayerOnAll_Test ()
{
_session->config.set_layer_model (AddHigher);
_session->config.set_relayer_on_all_edits (true);
create_short_regions ();
/* three overlapping regions */
_playlist->add_region (_region[A], 0);
_playlist->add_region (_region[B], 10);
_playlist->add_region (_region[C], 20);
/* and another non-overlapping one */
_playlist->add_region (_region[D], 200);
/* AddHigher means that they should be arranged thus */
CPPUNIT_ASSERT_EQUAL (layer_t (0), _region[A]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (1), _region[B]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (2), _region[C]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (0), _region[D]->layer ());
_region[A]->set_position (5);
/* region move should have no effect in AddHigher mode */
CPPUNIT_ASSERT_EQUAL (layer_t (0), _region[A]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (1), _region[B]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (2), _region[C]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (0), _region[D]->layer ());
/* C -> bottom should give C A B, not touching D */
_region[C]->lower_to_bottom ();
CPPUNIT_ASSERT_EQUAL (layer_t (0), _region[C]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (1), _region[A]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (2), _region[B]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (0), _region[D]->layer ());
/* C -> top should go back to A B C, not touching D */
_region[C]->raise_to_top ();
CPPUNIT_ASSERT_EQUAL (layer_t (0), _region[A]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (1), _region[B]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (2), _region[C]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (0), _region[D]->layer ());
}
void
PlaylistLayeringTest::addOrBoundsHigher_relayerOnAll_Test ()
{
_session->config.set_layer_model (AddOrBoundsChangeHigher);
_session->config.set_relayer_on_all_edits (true);
create_short_regions ();
/* three overlapping regions */
_playlist->add_region (_region[A], 0);
_playlist->add_region (_region[B], 10);
_playlist->add_region (_region[C], 20);
/* and another non-overlapping one */
_playlist->add_region (_region[D], 200);
/* AddOrBoundsHigher means that they should be arranged thus */
CPPUNIT_ASSERT_EQUAL (layer_t (0), _region[A]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (1), _region[B]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (2), _region[C]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (0), _region[D]->layer ());
/* region move should put A on top for B C A, not touching D */
_region[A]->set_position (5);
CPPUNIT_ASSERT_EQUAL (layer_t (0), _region[B]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (1), _region[C]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (2), _region[A]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (0), _region[D]->layer ());
/* C -> bottom should give C B A, not touching D */
_region[C]->lower_to_bottom ();
CPPUNIT_ASSERT_EQUAL (layer_t (0), _region[C]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (1), _region[B]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (2), _region[A]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (0), _region[D]->layer ());
/* C -> top should go back to B A C, not touching D */
_region[C]->raise_to_top ();
CPPUNIT_ASSERT_EQUAL (layer_t (0), _region[B]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (1), _region[A]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (2), _region[C]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (0), _region[D]->layer ());
/* Put C on the bottom */
_region[C]->lower_to_bottom ();
CPPUNIT_ASSERT_EQUAL (layer_t (0), _region[C]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (1), _region[B]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (2), _region[A]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (0), _region[D]->layer ());
/* Now move it slightly, and it should go back to the top again */
_region[C]->set_position (21);
CPPUNIT_ASSERT_EQUAL (layer_t (0), _region[B]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (1), _region[A]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (2), _region[C]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (0), _region[D]->layer ());
/* Put C back on the bottom */
_region[C]->lower_to_bottom ();
CPPUNIT_ASSERT_EQUAL (layer_t (0), _region[C]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (1), _region[B]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (2), _region[A]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (0), _region[D]->layer ());
/* Trim it slightly, and it should go back to the top again */
_region[C]->trim_front (23);
CPPUNIT_ASSERT_EQUAL (layer_t (0), _region[B]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (1), _region[A]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (2), _region[C]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (0), _region[D]->layer ());
/* Same with the end */
_region[C]->lower_to_bottom ();
CPPUNIT_ASSERT_EQUAL (layer_t (0), _region[C]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (1), _region[B]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (2), _region[A]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (0), _region[D]->layer ());
_region[C]->trim_end (118);
CPPUNIT_ASSERT_EQUAL (layer_t (0), _region[B]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (1), _region[A]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (2), _region[C]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (0), _region[D]->layer ());
}
void
PlaylistLayeringTest::addOrBoundsHigher_relayerWhenNecessary_Test ()
{
_session->config.set_layer_model (AddOrBoundsChangeHigher);
_session->config.set_relayer_on_all_edits (false);
create_short_regions ();
/* three overlapping regions */
_playlist->add_region (_region[A], 0);
_playlist->add_region (_region[B], 10);
_playlist->add_region (_region[C], 20);
/* and another non-overlapping one */
_playlist->add_region (_region[D], 200);
/* AddOrBoundsHigher means that they should be arranged thus */
CPPUNIT_ASSERT_EQUAL (layer_t (0), _region[A]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (1), _region[B]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (2), _region[C]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (0), _region[D]->layer ());
_region[A]->set_position (5);
/* region move should not have changed anything, since in
this mode we only relayer when there is a new overlap
*/
CPPUNIT_ASSERT_EQUAL (layer_t (0), _region[A]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (1), _region[B]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (2), _region[C]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (0), _region[D]->layer ());
/* C -> bottom should give C A B, not touching D */
_region[C]->lower_to_bottom ();
CPPUNIT_ASSERT_EQUAL (layer_t (0), _region[C]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (1), _region[A]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (2), _region[B]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (0), _region[D]->layer ());
/* C -> top should go back to A B C, not touching D */
_region[C]->raise_to_top ();
CPPUNIT_ASSERT_EQUAL (layer_t (0), _region[A]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (1), _region[B]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (2), _region[C]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (0), _region[D]->layer ());
/* Put C on the bottom */
_region[C]->lower_to_bottom ();
CPPUNIT_ASSERT_EQUAL (layer_t (0), _region[C]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (1), _region[A]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (2), _region[B]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (0), _region[D]->layer ());
/* Now move it slightly, and it should stay where it is */
_region[C]->set_position (21);
CPPUNIT_ASSERT_EQUAL (layer_t (0), _region[C]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (1), _region[A]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (2), _region[B]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (0), _region[D]->layer ());
}
void
PlaylistLayeringTest::lastLayerOpTest ()
{
create_short_regions ();
_playlist->add_region (_region[A], 0);
CPPUNIT_ASSERT_EQUAL (_playlist->layer_op_counter, _region[A]->last_layer_op (LayerOpAdd));
uint64_t const last_add = _region[A]->last_layer_op (LayerOpAdd);
_region[A]->set_position (42);
CPPUNIT_ASSERT_EQUAL (_playlist->layer_op_counter, _region[A]->last_layer_op (LayerOpBoundsChange));
CPPUNIT_ASSERT_EQUAL (last_add, _region[A]->last_layer_op (LayerOpAdd));
_region[A]->trim_front (46);
CPPUNIT_ASSERT_EQUAL (_playlist->layer_op_counter, _region[A]->last_layer_op (LayerOpBoundsChange));
CPPUNIT_ASSERT_EQUAL (last_add, _region[A]->last_layer_op (LayerOpAdd));
_region[A]->trim_end (102);
CPPUNIT_ASSERT_EQUAL (_playlist->layer_op_counter, _region[A]->last_layer_op (LayerOpBoundsChange));
CPPUNIT_ASSERT_EQUAL (last_add, _region[A]->last_layer_op (LayerOpAdd));
}
void
PlaylistLayeringTest::recursiveRelayerTest ()
{
_session->config.set_layer_model (AddOrBoundsChangeHigher);
_session->config.set_relayer_on_all_edits (false);
create_short_regions ();
_playlist->add_region (_region[A], 100);
_playlist->add_region (_region[B], 125);
_playlist->add_region (_region[C], 50);
_playlist->add_region (_region[D], 250);
CPPUNIT_ASSERT_EQUAL (layer_t (0), _region[A]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (1), _region[B]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (2), _region[C]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (0), _region[D]->layer ());
_region[A]->set_position (200);
CPPUNIT_ASSERT_EQUAL (layer_t (0), _region[D]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (1), _region[A]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (2), _region[B]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (3), _region[C]->layer ());
/* region move should have no effect */
CPPUNIT_ASSERT_EQUAL (layer_t (0), _region[0]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (1), _region[1]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (2), _region[2]->layer ());
}

View File

@ -1,6 +1,5 @@
#include <cppunit/TestFixture.h>
#include <cppunit/extensions/HelperMacros.h>
#include "test_needing_session.h"
namespace ARDOUR {
class Session;
@ -8,42 +7,23 @@ namespace ARDOUR {
class Source;
}
class PlaylistLayeringTest : public TestNeedingSession
class PlaylistLayeringTest : public CppUnit::TestFixture
{
CPPUNIT_TEST_SUITE (PlaylistLayeringTest);
CPPUNIT_TEST (lastLayerOpTest);
CPPUNIT_TEST (addHigher_relayerOnAll_Test);
CPPUNIT_TEST (addOrBoundsHigher_relayerOnAll_Test);
CPPUNIT_TEST (laterHigher_relayerOnAll_Test);
CPPUNIT_TEST (addOrBoundsHigher_relayerWhenNecessary_Test);
CPPUNIT_TEST (recursiveRelayerTest);
CPPUNIT_TEST (basicsTest);
CPPUNIT_TEST_SUITE_END ();
public:
void setUp ();
void tearDown ();
void lastLayerOpTest ();
void addHigher_relayerOnAll_Test ();
void addOrBoundsHigher_relayerOnAll_Test ();
void laterHigher_relayerOnAll_Test ();
void addOrBoundsHigher_relayerWhenNecessary_Test ();
void recursiveRelayerTest ();
void basicsTest ();
private:
void create_short_regions ();
static int const num_regions;
enum {
A = 0,
B,
C,
D,
E,
F
};
void create_three_short_regions ();
ARDOUR::Session* _session;
boost::shared_ptr<ARDOUR::Playlist> _playlist;
boost::shared_ptr<ARDOUR::Source> _source;
boost::shared_ptr<ARDOUR::Region>* _region;
boost::shared_ptr<ARDOUR::Region> _region[16];
};

View File

@ -1,119 +0,0 @@
#include "pbd/compose.h"
#include "ardour/playlist.h"
#include "ardour/playlist_factory.h"
#include "ardour/source_factory.h"
#include "ardour/region.h"
#include "ardour/region_sorters.h"
#include "ardour/region_factory.h"
#include "playlist_overlap_cache_test.h"
using namespace std;
using namespace PBD;
using namespace ARDOUR;
CPPUNIT_TEST_SUITE_REGISTRATION (PlaylistOverlapCacheTest);
void
PlaylistOverlapCacheTest::tearDown ()
{
_playlist.reset ();
_source.reset ();
TestNeedingSession::tearDown ();
}
void
PlaylistOverlapCacheTest::basicTest ()
{
string const test_wav_path = "libs/ardour/test/test.wav";
_playlist = PlaylistFactory::create (DataType::AUDIO, *_session, "test");
_source = SourceFactory::createWritable (DataType::AUDIO, *_session, test_wav_path, "", false, 44100);
PropertyList plist;
plist.add (Properties::length, 256);
boost::shared_ptr<Region> regionA = RegionFactory::create (_source, plist);
regionA->set_name ("A");
_playlist->add_region (regionA, 0);
{
Playlist::OverlapCache cache (_playlist.get ());
Playlist::RegionList rl = cache.get (Evoral::Range<framepos_t> (0, 256));
CPPUNIT_ASSERT_EQUAL (size_t (1), rl.size ());
CPPUNIT_ASSERT_EQUAL (regionA, rl.front ());
rl = cache.get (Evoral::Range<framepos_t> (-1000, 1000));
CPPUNIT_ASSERT_EQUAL (size_t (1), rl.size ());
CPPUNIT_ASSERT_EQUAL (regionA, rl.front ());
}
boost::shared_ptr<Region> regionB = RegionFactory::create (_source, plist);
regionA->set_name ("B");
_playlist->add_region (regionB, 53);
{
Playlist::OverlapCache cache (_playlist.get ());
Playlist::RegionList rl = cache.get (Evoral::Range<framepos_t> (0, 256));
CPPUNIT_ASSERT_EQUAL (size_t (2), rl.size ());
rl.sort (RegionSortByPosition ());
CPPUNIT_ASSERT_EQUAL (regionA, rl.front ());
CPPUNIT_ASSERT_EQUAL (regionB, rl.back ());
rl = cache.get (Evoral::Range<framepos_t> (260, 274));
CPPUNIT_ASSERT_EQUAL (size_t (1), rl.size ());
CPPUNIT_ASSERT_EQUAL (regionB, rl.front ());
}
}
void
PlaylistOverlapCacheTest::stressTest ()
{
string const test_wav_path = "libs/ardour/test/test.wav";
_playlist = PlaylistFactory::create (DataType::AUDIO, *_session, "test");
_source = SourceFactory::createWritable (DataType::AUDIO, *_session, test_wav_path, "", false, 44100);
srand (42);
int const num_regions = rand () % 256;
for (int i = 0; i < num_regions; ++i) {
PropertyList plist;
plist.add (Properties::length, rand () % 32768);
boost::shared_ptr<Region> r = RegionFactory::create (_source, plist);
r->set_name (string_compose ("%1", i));
_playlist->add_region (r, rand() % 32768);
}
Playlist::OverlapCache cache (_playlist.get ());
int const tests = rand () % 256;
for (int i = 0; i < tests; ++i) {
framepos_t const start = rand () % 32768;
framepos_t const length = rand () % 32768;
framepos_t const end = start + length;
Playlist::RegionList cached = cache.get (Evoral::Range<framepos_t> (start, end));
Playlist::RegionList actual;
Playlist::RegionList regions = _playlist->region_list().rlist();
for (Playlist::RegionList::iterator j = regions.begin(); j != regions.end(); ++j) {
if ((*j)->coverage (start, end) != OverlapNone) {
actual.push_back (*j);
}
}
cached.sort (RegionSortByPosition ());
actual.sort (RegionSortByPosition ());
CPPUNIT_ASSERT_EQUAL (actual.size (), cached.size ());
Playlist::RegionList::iterator j = actual.begin ();
Playlist::RegionList::iterator k = cached.begin ();
for (; j != actual.end(); ++j, ++k) {
CPPUNIT_ASSERT_EQUAL (*j, *k);
}
}
}

View File

@ -1,20 +0,0 @@
#include "test_needing_session.h"
class PlaylistOverlapCacheTest : public TestNeedingSession
{
public:
CPPUNIT_TEST_SUITE (PlaylistOverlapCacheTest);
CPPUNIT_TEST (basicTest);
CPPUNIT_TEST (stressTest);
CPPUNIT_TEST_SUITE_END ();
public:
void tearDown ();
void basicTest ();
void stressTest ();
private:
boost::shared_ptr<ARDOUR::Playlist> _playlist;
boost::shared_ptr<ARDOUR::Source> _source;
};

View File

@ -1,48 +0,0 @@
#include "midi++/manager.h"
#include "pbd/textreceiver.h"
#include "pbd/compose.h"
#include "pbd/enumwriter.h"
#include "ardour/session.h"
#include "ardour/audioengine.h"
#include "test_needing_session.h"
#include "test_receiver.h"
using namespace std;
using namespace ARDOUR;
using namespace PBD;
TestReceiver test_receiver;
void
TestNeedingSession::setUp ()
{
string const test_session_path = "libs/ardour/test/test_session";
system (string_compose ("rm -rf %1", test_session_path).c_str());
init (false, true);
SessionEvent::create_per_thread_pool ("test", 512);
test_receiver.listen_to (error);
test_receiver.listen_to (info);
test_receiver.listen_to (fatal);
test_receiver.listen_to (warning);
AudioEngine* engine = new AudioEngine ("test", "");
MIDI::Manager::create (engine->jack ());
CPPUNIT_ASSERT (engine->start () == 0);
_session = new Session (*engine, test_session_path, "test_session");
engine->set_session (_session);
}
void
TestNeedingSession::tearDown ()
{
AudioEngine::instance()->remove_session ();
delete _session;
EnumWriter::destroy ();
MIDI::Manager::destroy ();
AudioEngine::destroy ();
}

View File

@ -1,16 +0,0 @@
#include <cppunit/TestFixture.h>
#include <cppunit/extensions/HelperMacros.h>
namespace ARDOUR {
class Session;
}
class TestNeedingSession : public CppUnit::TestFixture
{
public:
void setUp ();
void tearDown ();
protected:
ARDOUR::Session* _session;
};

View File

@ -1,37 +0,0 @@
#include "pbd/receiver.h"
class TestReceiver : public Receiver
{
protected:
void receive (Transmitter::Channel chn, const char * str) {
const char *prefix = "";
switch (chn) {
case Transmitter::Error:
prefix = ": [ERROR]: ";
break;
case Transmitter::Info:
/* ignore */
return;
case Transmitter::Warning:
prefix = ": [WARNING]: ";
break;
case Transmitter::Fatal:
prefix = ": [FATAL]: ";
break;
case Transmitter::Throw:
/* this isn't supposed to happen */
abort ();
}
/* note: iostreams are already thread-safe: no external
lock required.
*/
std::cout << prefix << str << std::endl;
if (chn == Transmitter::Fatal) {
exit (9);
}
}
};

View File

@ -431,8 +431,6 @@ def build(bld):
test/framepos_plus_beats_test.cc
test/framepos_minus_beats_test.cc
test/playlist_layering_test.cc
test/playlist_overlap_cache_test.cc
test/test_needing_session.cc
test/testrunner.cc
'''.split()

View File

@ -21,8 +21,8 @@
</para>
<para>
Of course, nothing in digital audio is ever quite that simple, and so
there are some complications:
Of course, nothing in digital audio is ever quite that simple, and so of
course there are some complications:
</para>
<section id="layers-crossfades">
@ -38,14 +38,14 @@
<section id="region-opacity">
<title> Region Opacity </title>
<para>
With a nod to image manipulation programs, Ardour allows you to
In a perverse nod to image manipulation programs, Ardour allows you to
make regions transparent. By default, all regions are created opaque,
which means that when they are playing, no region below them are
audible. However, if you change the region to be transparent, the
region will be audible together with any regions below it. This
capability should probably not be abused; if you really want to mix
sounds together in this way, they should probably be on their own
tracks. Occasionally though, this can be a useful trick.
capability should probably not be abused - if you really want to mix
sounds together in this way, they should probably live in their own
tracks. Occasionally though, this can be useful trick.
</para>
<para>
@ -55,74 +55,70 @@
</para>
</section>
<section id="choice-of-layering">
<title>Choice of layering</title>
<section id="layering-styles">
<title> Layering Styles </title>
<para>
When you are recording new material for a track, its typical to want
to new material recorded "over" existing material in the track to be
what you hear on playback. For example, if you overdub part of a
guitar solo, you normally want the overdub to be audible, not hidden
by the old version that was already there. By contrast, when editing
using splitting/trimming/moving of regions to create a particular
arrangement along the timeline, many people find that they want
regions that start later on the timeline to be the ones that are
audible.
</para>
<para>
There are two main decisions to be made with regard to how a playlist
should be layered:
To facilitate these two contradictory desires, Ardour features three
different styles for assigning regions to layers.
</para>
<variablelist>
<title></title>
<varlistentry>
Given overlapping regions, which order should they be layered in?
<term>Most recently added regions are higher</term>
<listitem>
<para>
Use this style when recording/overdubbing new material. Edits of
any kind do not modify the layering.
</para>
</listitem>
</varlistentry>
<varlistentry>
When should layering be changed?
<term>Most recently added/moved/trimmed regions are higher</term>
<listitem>
<para>
Use this style when recording/overdubbing new material, but you
want basic edits to cause regions to rise to the top.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>Later regions are higher</term>
<listitem>
<para>
Use this style when rearranging and editing regions.
</para>
</listitem>
</varlistentry>
</variablelist>
<section id="layering-order">
<title>Layering Order</title>
<para>
Ardour provides three-and-a-half ways to decide on the order in which regions are layered. The most basic choice is:
</para>
<variablelist>
<title></title>
<varlistentry>
<term>Most recently added regions are higher</term>
<listitem>
<para>
Regions which are later in time will be on higher layers.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>Most recently added or edited regions are higher</term>
<listitem>
<para>
Regions which were more recently edited or added to the playlist
will be on higher layers.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>Later regions are higher</term>
<listitem>
<para>
Regions which were more recently added to the playlist will be on higher
layers.
</para>
</listitem>
</varlistentry>
</variablelist>
</section>
<para>
A new session has the layering style set to "Most recently edited or
added regions are higher". To change the layering style, open the
<emphasis>Session Properties</emphasis> dialogue and choose your layering
style from the "Misc" page. Changing the layering style only affects
future edits to the playlist; the existing layering of all playlists is
preserved when changing the layering mode.
A new session has the layering style set to "Most recently
added/moved/trimmed regions are higher". To change the layering style,
open the <emphasis>options editor</emphasis> and select the
"Layers&amp;Fades" page. There is an option there to select the style
you want. Layering style may be changed at any time. The existing
layering of all playlists is not changed when changing the layering
model.
</para>
</section>
<section id="modifying-layering-by-hand">
<title>Modifying Layering Explicitly</title>
<title> Modifying Layering By Hand </title>
<para>
If you want a particular region to be the uppermost when the current
layering style has put it on a lower layer, context click on the