# HG changeset patch # User Edouard Tisserant # Date 1664349547 -7200 # Node ID bb1eff4091ab2804fa4d777dce63075698eb1381 # Parent 770c613c424ffe2c8f9b3f0ebe601cbca6c49d1d SVGHMI: add support for "reference" and "frame" rectangles to spread-out ovelapping elements. When HMI become complicated, designer needs to spread-out ovelapping elements in order to unclutter drawing and facilitate maintenance. diff -r 770c613c424f -r bb1eff4091ab exemples/svghmi_references/beremiz.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/exemples/svghmi_references/beremiz.xml Wed Sep 28 09:19:07 2022 +0200 @@ -0,0 +1,5 @@ + + + + + diff -r 770c613c424f -r bb1eff4091ab exemples/svghmi_references/plc.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/exemples/svghmi_references/plc.xml Wed Sep 28 09:19:07 2022 +0200 @@ -0,0 +1,71 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + LocalVar0 + + + + + + + + + + + LocalVar1 + + + + + + + + + + + + + + + + + + diff -r 770c613c424f -r bb1eff4091ab exemples/svghmi_references/svghmi_0@svghmi/baseconfnode.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/exemples/svghmi_references/svghmi_0@svghmi/baseconfnode.xml Wed Sep 28 09:19:07 2022 +0200 @@ -0,0 +1,2 @@ + + diff -r 770c613c424f -r bb1eff4091ab exemples/svghmi_references/svghmi_0@svghmi/confnode.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/exemples/svghmi_references/svghmi_0@svghmi/confnode.xml Wed Sep 28 09:19:07 2022 +0200 @@ -0,0 +1,2 @@ + + diff -r 770c613c424f -r bb1eff4091ab exemples/svghmi_references/svghmi_0@svghmi/svghmi.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/exemples/svghmi_references/svghmi_0@svghmi/svghmi.svg Wed Sep 28 09:19:07 2022 +0200 @@ -0,0 +1,1236 @@ + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + Switch widget + + + + + + Home + + + + + Swith + + + + + Buttons + + + + declaration of "position" HMI local variable + + + + + Show popup 1 + + + + Show popup 2 + + + + + + + + + Togglebutton + + + + + + + + + Button + + + + + + + + + PushButton + + + + + + + Notes: - Widget roles are described in objects labels.- Press Ctrl+O to open object properties panel- To see objects in a tree, select Object->Objects in menu- Inkscape's "objects" are SVG elements- Press Ctrl+X to edit SVG elements directly with XML editor + + + + + Page + + + + final position in page + offset positionfor "B" + offset positionfor "C" + HMI:Switch@... (group) |-. "A" (group) | |- reference (rect) | |- ... |-. "B" (group) | |- frame (rect) | |- ... |-. "C" (group) | |- frame (rect) | |- ... + Some widgets like Switch or Button are displaying one of many groups that represent the possible states of the widget.Since all groups need to appear in the same place, they overlap and the drawing becomes hard to understand and maintain.Using specially labelled "reference" and "frame" rectangles, groups can be spread out. + reference + frame + frame + + + + + + + + + Close + + + A MODAL DIALOG + + + + + + + + + + Close + + + A MODAL DIALOGwith widgets + + + + 0 + + + + 1 + + + + 2 + + + + 3 + + 0 + position + + + + + + + + + + + declaration of user_level HMI local variable(not a PLC variable) + + + declaration of "range" HMI local variable + + + declaration of "size" HMI local variable + + Button widgets + + declaration of "position" HMI local variable + + diff -r 770c613c424f -r bb1eff4091ab svghmi/geometry.ysl2 --- a/svghmi/geometry.ysl2 Thu Sep 22 09:42:38 2022 +0200 +++ b/svghmi/geometry.ysl2 Wed Sep 28 09:19:07 2022 +0200 @@ -145,3 +145,16 @@ result """$candidates[(@Id = $groups/@id and (func:intersect($g, .) = 9)) or (not(@Id = $groups/@id) and (func:intersect($g, .) > 0 ))]"""; } + +def "func:offset" { + param "elt1"; + param "elt2"; + const "g1", "$geometry[@Id = $elt1/@id]"; + const "g2", "$geometry[@Id = $elt2/@id]"; + const "result" vector { + attrib "x" value "$g2/@x - $g1/@x"; + attrib "y" value "$g2/@y - $g1/@y"; + } + result "exsl:node-set($result)"; +} + diff -r 770c613c424f -r bb1eff4091ab svghmi/inline_svg.ysl2 --- a/svghmi/inline_svg.ysl2 Thu Sep 22 09:42:38 2022 +0200 +++ b/svghmi/inline_svg.ysl2 Wed Sep 28 09:19:07 2022 +0200 @@ -42,6 +42,23 @@ attrib "{name()}" > «substring(., 2)» } +// remove "reference" and "frame" rectangles +svgtmpl "svg:rect[@inkscape:label='reference' or @inkscape:label='frame']", mode="inline_svg" { + // nothing +} + +svgtmpl "svg:g[svg:rect/@inkscape:label='frame']", mode="inline_svg" { + const "reference_rect","(../svg:rect | ../svg:g/svg:rect)[@inkscape:label='reference']"; + const "frame_rect","svg:rect[@inkscape:label='frame']"; + const "offset","func:offset($frame_rect, $reference_rect)"; + + xsl:copy { + attrib "svghmi_x_offset" value "$offset/vector/@x"; + attrib "svghmi_y_offset" value "$offset/vector/@y"; + apply "@* | node()", mode="inline_svg"; + } +} + ////// Clone unlinking // // svg:use (inkscape's clones) inside a widgets are diff -r 770c613c424f -r bb1eff4091ab svghmi/svghmi.js --- a/svghmi/svghmi.js Thu Sep 22 09:42:38 2022 +0200 +++ b/svghmi/svghmi.js Wed Sep 28 09:19:07 2022 +0200 @@ -532,8 +532,49 @@ current_visible_page = page_name; }; +/* From https://jsfiddle.net/ibowankenobi/1mmh7rs6/6/ */ +function getAbsoluteCTM(element){ + var height = svg_root.height.baseVal.value, + width = svg_root.width.baseVal.value, + viewBoxRect = svg_root.viewBox.baseVal, + vHeight = viewBoxRect.height, + vWidth = viewBoxRect.width; + if(!vWidth || !vHeight){ + return element.getCTM(); + } + var sH = height/vHeight, + sW = width/vWidth, + matrix = svg_root.createSVGMatrix(); + matrix.a = sW; + matrix.d = sH + var realCTM = element.getCTM().multiply(matrix.inverse()); + realCTM.e = realCTM.e/sW + viewBoxRect.x; + realCTM.f = realCTM.f/sH + viewBoxRect.y; + return realCTM; +} + +function apply_reference_frames(){ + const matches = svg_root.querySelectorAll("g[svghmi_x_offset]"); + matches.forEach((group) => { + let [x,y] = ["x", "y"].map((axis) => Number(group.getAttribute("svghmi_"+axis+"_offset"))); + let ctm = getAbsoluteCTM(group); + // zero translation part of CTM + // to only apply rotation/skewing to offset vector + ctm.e = 0; + ctm.f = 0; + let invctm = ctm.inverse(); + let vect = new DOMPoint(x, y); + let newvect = vect.matrixTransform(invctm); + let transform = svg_root.createSVGTransform(); + transform.setTranslate(newvect.x, newvect.y); + group.transform.baseVal.appendItem(transform); + ["x", "y"].forEach((axis) => group.removeAttribute("svghmi_"+axis+"_offset")); + }); +} + // Once connection established ws.onopen = function (evt) { + apply_reference_frames(); init_widgets(); send_reset(); // show main page