SVGHMI: detachable and discardable elements sets, Reworked geometric intersection, toward more accurate page content detection.
Moved page's widget/element dependency crawling functions so that it is possible to compute a global detachable and discardable elements sets.
Reworked geometric intersection detection logic to distinguish ovelapping and inclusion.
Goal is to include englobing and overlapping graphical elements, but not groups (would then include everything around...). Intermediate commit, to be continued.
--- a/svghmi/gen_index_xhtml.xslt Thu Feb 27 13:14:24 2020 +0100
+++ b/svghmi/gen_index_xhtml.xslt Fri Feb 28 16:09:21 2020 +0100
@@ -47,6 +47,90 @@
<xsl:apply-templates mode="index" select="$hmitree"/>
</xsl:variable>
<xsl:variable name="indexed_hmitree" select="exsl:node-set($_indexed_hmitree)"/>
+ <func:function name="func:refered_elements">
+ <xsl:param name="elems"/>
+ <xsl:variable name="descend" select="$elems/descendant-or-self::svg:*"/>
+ <xsl:variable name="clones" select="$descend[self::svg:use]"/>
+ <xsl:variable name="originals" select="//svg:*[concat('#',@id) = $clones/@xlink:href]"/>
+ <xsl:choose>
+ <xsl:when test="$originals">
+ <func:result select="$descend | func:refered_elements($originals)"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <func:result select="$descend"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </func:function>
+ <func:function name="func:intersect_1d">
+ <xsl:param name="a0"/>
+ <xsl:param name="a1"/>
+ <xsl:param name="b0"/>
+ <xsl:param name="b1"/>
+ <xsl:variable name="d0" select="$a0 >= $b0"/>
+ <xsl:variable name="d1" select="$a1 >= $b1"/>
+ <xsl:choose>
+ <xsl:when test="not($d0) and $d1">
+ <func:result select="3"/>
+ </xsl:when>
+ <xsl:when test="$d0 and not($d1)">
+ <func:result select="2"/>
+ </xsl:when>
+ <xsl:when test="$d0 = $d1 and $b0 < $a1">
+ <func:result select="1"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <func:result select="0"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </func:function>
+ <func:function name="func:intersect">
+ <xsl:param name="a"/>
+ <xsl:param name="b"/>
+ <xsl:variable name="x_intersect" select="func:intersect_1d($a/@x, $a/@x+$a/@w, $b/@x, $b/@x+$b/@w)"/>
+ <xsl:choose>
+ <xsl:when test="$x_intersect != 0">
+ <xsl:variable name="y_intersect" select="func:intersect_1d($a/@y, $a/@y+$a/@w, $b/@y, $b/@y+$b/@w)"/>
+ <func:result select="$x_intersect * $y_intersect"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <func:result select="0"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </func:function>
+ <func:function name="func:overlapping_geometry">
+ <xsl:param name="elt"/>
+ <xsl:variable name="g" select="$geometry[@Id = $elt/@id]"/>
+ <func:result select="$geometry[@Id != $elt/@id and func:intersect(., $g) = 4]"/>
+ </func:function>
+ <func:function name="func:sumarized_elements">
+ <xsl:param name="elements"/>
+ <xsl:variable name="short_list" select="$elements[not(ancestor::*/@id = $elements/@id)]"/>
+ <xsl:variable name="filled_groups" select="$short_list/parent::svg:*[not(descendant::*[not(self::svg:g)][not(@id = $short_list/descendant-or-self::*[not(self::svg:g)]/@id)])]"/>
+ <xsl:variable name="groups_to_add" select="$filled_groups[not(ancestor::*/@id = $filled_groups/@id)]"/>
+ <func:result select="$groups_to_add | $short_list[not(ancestor::svg:g/@id = $filled_groups/@id)]"/>
+ </func:function>
+ <func:function name="func:all_related_elements">
+ <xsl:param name="page"/>
+ <xsl:variable name="page_overlapping_geometry" select="func:overlapping_geometry($page)"/>
+ <xsl:variable name="page_overlapping_elements" select="//svg:*[@id = $page_overlapping_geometry/@Id]"/>
+ <xsl:variable name="page_sub_elements" select="func:refered_elements($page | $page_overlapping_elements)"/>
+ <func:result select="$page_sub_elements"/>
+ </func:function>
+ <func:function name="func:detachable_elements">
+ <xsl:param name="pages"/>
+ <xsl:choose>
+ <xsl:when test="$pages">
+ <func:result select="func:sumarized_elements(func:all_related_elements($pages[1])) | func:detachable_elements($pages[position()!=1])"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <func:result select="/.."/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </func:function>
+ <xsl:variable name="detachable_elements" select="func:detachable_elements($hmi_pages)"/>
+ <xsl:variable name="essential_elements" select="$detachable_elements | /svg:svg/svg:defs"/>
+ <xsl:variable name="required_elements" select="$essential_elements//svg:* | $essential_elements/ancestor-or-self::svg:*"/>
+ <xsl:variable name="discardable_elements" select="//svg:*[not(@id = $required_elements/@id)]"/>
<xsl:template mode="index" match="*">
<xsl:param name="index" select="0"/>
<xsl:param name="parentpath" select="''"/>
@@ -139,6 +223,24 @@
<xsl:comment>
<xsl:apply-templates mode="testtree" select="$indexed_hmitree"/>
</xsl:comment>
+ <xsl:comment>
+ <xsl:text>Detachable :
+</xsl:text>
+ <xsl:for-each select="$detachable_elements">
+ <xsl:value-of select="@id"/>
+ <xsl:text>
+</xsl:text>
+ </xsl:for-each>
+ </xsl:comment>
+ <xsl:comment>
+ <xsl:text>Discardable :
+</xsl:text>
+ <xsl:for-each select="$discardable_elements">
+ <xsl:value-of select="@id"/>
+ <xsl:text>
+</xsl:text>
+ </xsl:for-each>
+ </xsl:comment>
<html xmlns:svg="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/1999/xhtml">
<head/>
<body style="margin:0;overflow:hidden;">
@@ -200,39 +302,6 @@
</xsl:variable>
<func:result select="exsl:node-set($ast)"/>
</func:function>
- <func:function name="func:refered_elements">
- <xsl:param name="elems"/>
- <xsl:variable name="descend" select="$elems/descendant-or-self::svg:*"/>
- <xsl:variable name="clones" select="$descend[self::svg:use]"/>
- <xsl:variable name="originals" select="//svg:*[concat('#',@id) = $clones/@xlink:href]"/>
- <xsl:choose>
- <xsl:when test="$originals">
- <func:result select="$descend | func:refered_elements($originals)"/>
- </xsl:when>
- <xsl:otherwise>
- <func:result select="$descend"/>
- </xsl:otherwise>
- </xsl:choose>
- </func:function>
- <func:function name="func:included_geometry">
- <xsl:param name="elt"/>
- <xsl:variable name="g" select="$geometry[@Id = $elt/@id]"/>
- <func:result select="$geometry[@Id != $elt/@id and @x >= $g/@x and @y >= $g/@y and @x+@w <= $g/@x+$g/@w and @y+@h <= $g/@y+$g/@h]"/>
- </func:function>
- <func:function name="func:sumarized_elements">
- <xsl:param name="elements"/>
- <xsl:variable name="short_list" select="$elements[not(ancestor::*/@id = $elements/@id)]"/>
- <xsl:variable name="filled_groups" select="$short_list/parent::svg:*[not(descendant::*[not(self::svg:g)][not(@id = $short_list/descendant-or-self::*[not(self::svg:g)]/@id)])]"/>
- <xsl:variable name="groups_to_add" select="$filled_groups[not(ancestor::*/@id = $filled_groups/@id)]"/>
- <func:result select="$groups_to_add | $short_list[not(ancestor::svg:g/@id = $filled_groups/@id)]"/>
- </func:function>
- <func:function name="func:all_related_elements">
- <xsl:param name="page"/>
- <xsl:variable name="page_included_geometry" select="func:included_geometry($page)"/>
- <xsl:variable name="page_sub_elements" select="func:refered_elements($page)"/>
- <xsl:variable name="page_included_elements" select="//svg:*[@id = $page_included_geometry/@Id]"/>
- <func:result select="$page_sub_elements | $page_included_elements"/>
- </func:function>
<xsl:template name="scripts">
<xsl:text>//(function(){
</xsl:text>
@@ -377,7 +446,7 @@
<xsl:text>
</xsl:text>
</xsl:for-each>
- <xsl:text> ]
+ <xsl:text> ],
</xsl:text>
<xsl:text> required_elements: [
</xsl:text>
@@ -846,11 +915,27 @@
</xsl:text>
<xsl:text>
</xsl:text>
+ <xsl:text>// function prepare_svg() {
+</xsl:text>
+ <xsl:text>// /* set everybody hidden initially for better performance */
+</xsl:text>
+ <xsl:text>// for(let [elt,elt_parent] in detachable_elements){
+</xsl:text>
+ <xsl:text>// elt_parent.removeChild(elt)
+</xsl:text>
+ <xsl:text>// }
+</xsl:text>
+ <xsl:text>// };
+</xsl:text>
+ <xsl:text>
+</xsl:text>
<xsl:text>function prepare_svg() {
</xsl:text>
<xsl:text> /* set everybody hidden initially for better performance */
</xsl:text>
- <xsl:text> for(let widget in hmi_widgets){
+ <xsl:text> for(let widget_id in hmi_widgets){
+</xsl:text>
+ <xsl:text> let widget = hmi_widgets[widget_id];
</xsl:text>
<xsl:text> if(widget.element != undefined)
</xsl:text>
@@ -858,16 +943,6 @@
</xsl:text>
<xsl:text> }
</xsl:text>
- <xsl:text> /*for(let name in page_desc){
-</xsl:text>
- <xsl:text> if(name != new_desc){
-</xsl:text>
- <xsl:text> page_desc[name].widget.element.style.display = "none";
-</xsl:text>
- <xsl:text> }
-</xsl:text>
- <xsl:text> }*/
-</xsl:text>
<xsl:text>};
</xsl:text>
<xsl:text>
--- a/svghmi/gen_index_xhtml.ysl2 Thu Feb 27 13:14:24 2020 +0100
+++ b/svghmi/gen_index_xhtml.ysl2 Fri Feb 28 16:09:21 2020 +0100
@@ -73,8 +73,98 @@
const "indexed_hmitree", "exsl:node-set($_indexed_hmitree)";
- // TODO globally discardable elements, not (used by | ancestor of) any page
-
+ // returns all directly or indirectly refered elements
+ def "func:refered_elements" {
+ param "elems";
+ const "descend", "$elems/descendant-or-self::svg:*";
+ const "clones", "$descend[self::svg:use]";
+ const "originals", "//svg:*[concat('#',@id) = $clones/@xlink:href]";
+ choose {
+ when "$originals"
+ result "$descend | func:refered_elements($originals)";
+ otherwise
+ result "$descend";
+ }
+ }
+
+ def "func:intersect_1d" {
+ /* it is assumed that a1 > a0 and b1 > b0 */
+ param "a0";
+ param "a1";
+ param "b0";
+ param "b1";
+
+ const "d0", "$a0 >= $b0";
+ const "d1", "$a1 >= $b1";
+ choose {
+ when "not($d0) and $d1"
+ result "3"; /* a included in b */
+ when "$d0 and not($d1)"
+ result "2"; /* b included in a */
+ when "$d0 = $d1 and $b0 < $a1"
+ result "1"; /* a and b are overlapped */
+ otherwise
+ result "0"; /* no intersection*/
+ }
+ }
+
+ def "func:intersect" {
+ param "a";
+ param "b";
+
+ const "x_intersect", "func:intersect_1d($a/@x, $a/@x+$a/@w, $b/@x, $b/@x+$b/@w)";
+
+ choose{
+ when "$x_intersect != 0"{
+ const "y_intersect", "func:intersect_1d($a/@y, $a/@y+$a/@w, $b/@y, $b/@y+$b/@w)";
+ result "$x_intersect * $y_intersect";
+ }
+ otherwise result "0";
+ }
+ }
+
+ // return overlapping geometry a given element
+ def "func:overlapping_geometry" {
+ param "elt";
+ /* only included groups are returned */
+ /* all other elemenst are returne when overlapping*/
+ const "g", "$geometry[@Id = $elt/@id]";
+ result """$geometry[@Id != $elt/@id and func:intersect(., $g) = 4]""";
+ }
+
+ def "func:sumarized_elements" {
+ param "elements";
+ const "short_list", "$elements[not(ancestor::*/@id = $elements/@id)]";
+ /* TODO exclude globally discardable elements from group fulfillment check */
+ const "filled_groups", "$short_list/parent::svg:*[not(descendant::*[not(self::svg:g)][not(@id = $short_list/descendant-or-self::*[not(self::svg:g)]/@id)])]";
+ const "groups_to_add", "$filled_groups[not(ancestor::*/@id = $filled_groups/@id)]";
+ result "$groups_to_add | $short_list[not(ancestor::svg:g/@id = $filled_groups/@id)]";
+ }
+
+ def "func:all_related_elements" {
+ param "page";
+ const "page_overlapping_geometry", "func:overlapping_geometry($page)";
+ const "page_overlapping_elements", "//svg:*[@id = $page_overlapping_geometry/@Id]";
+ const "page_sub_elements", "func:refered_elements($page | $page_overlapping_elements)";
+ result "$page_sub_elements";
+ }
+
+ def "func:detachable_elements" {
+ param "pages";
+ choose{
+ when "$pages"{
+ result """func:sumarized_elements(func:all_related_elements($pages[1]))
+ | func:detachable_elements($pages[position()!=1])""";
+ }otherwise{
+ result "/..";
+ }
+ }
+ }
+
+ const "detachable_elements", "func:detachable_elements($hmi_pages)";
+ const "essential_elements", "$detachable_elements | /svg:svg/svg:defs";
+ const "required_elements", "$essential_elements//svg:* | $essential_elements/ancestor-or-self::svg:*";
+ const "discardable_elements", "//svg:*[not(@id = $required_elements/@id)]";
template "*", mode="index" {
param "index", "0";
@@ -193,6 +283,18 @@
comment {
apply "$indexed_hmitree", mode="testtree";
}
+ comment {
+ | Detachable :
+ foreach "$detachable_elements"{
+ | «@id»
+ }
+ }
+ comment {
+ | Discardable :
+ foreach "$discardable_elements"{
+ | «@id»
+ }
+ }
/**/
html xmlns="http://www.w3.org/1999/xhtml"
xmlns:svg="http://www.w3.org/2000/svg"
@@ -254,48 +356,6 @@
result "exsl:node-set($ast)";
}
- // returns all directly or indirectly refered elements
- def "func:refered_elements" {
- param "elems";
- const "descend", "$elems/descendant-or-self::svg:*";
- const "clones", "$descend[self::svg:use]";
- const "originals", "//svg:*[concat('#',@id) = $clones/@xlink:href]";
- choose {
- when "$originals"
- result "$descend | func:refered_elements($originals)";
- otherwise
- result "$descend";
- }
- }
-
- // return included geometry a given element
- def "func:included_geometry" {
- param "elt";
- const "g", "$geometry[@Id = $elt/@id]";
- result """$geometry[@Id != $elt/@id and
- @x >= $g/@x and @y >= $g/@y and
- @x+@w <= $g/@x+$g/@w and @y+@h <= $g/@y+$g/@h]""";
-
- }
-
- def "func:sumarized_elements" {
- param "elements";
- const "short_list", "$elements[not(ancestor::*/@id = $elements/@id)]";
- /* TODO exclude globally discardable elements from group fulfillment check */
- const "filled_groups", "$short_list/parent::svg:*[not(descendant::*[not(self::svg:g)][not(@id = $short_list/descendant-or-self::*[not(self::svg:g)]/@id)])]";
- const "groups_to_add", "$filled_groups[not(ancestor::*/@id = $filled_groups/@id)]";
- result "$groups_to_add | $short_list[not(ancestor::svg:g/@id = $filled_groups/@id)]";
- }
-
- def "func:all_related_elements" {
- param "page";
- const "page_included_geometry", "func:included_geometry($page)";
- const "page_sub_elements", "func:refered_elements($page)";
-
- const "page_included_elements", "//svg:*[@id = $page_included_geometry/@Id]";
- result "$page_sub_elements | $page_included_elements";
- }
-
function "scripts"
{
| //(function(){
@@ -380,7 +440,7 @@
foreach "$all_page_ids" {
| hmi_widgets["«.»"]`if "position()!=last()" > ,`
}
- | ]
+ | ],
| required_elements: [
foreach "$shorter_list" {
| "«@id»",