beremiz

SVGHMI: Filter unseen geometry from inkscape CSV output.
svghmi
2021-03-25, Edouard Tisserant
1582753e409b
SVGHMI: Filter unseen geometry from inkscape CSV output.

When inkscape exports geometry form all objects, then it also includes objects from svg:defs. This makes problems when deciding if an object is part of a page, since coordinate of objects in svg:defs can eventualy be contained in a page. In the end, those objects where getting detached when leaving pages where they where found, leading for exemple to non working text on clipping when the clipped text was cloned in multiple page.
// widget_jsontable.ysl2
template "widget[@type='JsonTable']", mode="widget_class"
||
class JsonTableWidget extends Widget{
// arbitrary defaults to avoid missing entries in query
cache = [0,0,0];
init_common() {
this.spread_json_data_bound = this.spread_json_data.bind(this);
this.handle_http_response_bound = this.handle_http_response.bind(this);
this.fetch_error_bound = this.fetch_error.bind(this);
this.promised = false;
}
handle_http_response(response) {
if (!response.ok) {
console.log("HTTP error, status = " + response.status);
}
return response.json();
}
fetch_error(e){
console.log("HTTP fetch error, message = " + e.message + "Widget:" + this.element_id);
}
do_http_request(...opt) {
this.abort_controller = new AbortController();
return Promise.resolve().then(() => {
const query = {
args: this.args,
range: this.cache[1],
position: this.cache[2],
visible: this.visible,
extra: this.cache.slice(4),
options: opt
};
const options = {
method: 'POST',
body: JSON.stringify(query),
headers: {'Content-Type': 'application/json'},
signal: this.abort_controller.signal
};
return fetch(this.args[0], options)
.then(this.handle_http_response_bound)
.then(this.spread_json_data_bound)
.catch(this.fetch_error_bound);
});
}
unsub(){
this.abort_controller.abort();
super.unsub();
}
sub(...args){
this.cache[0] = undefined;
super.sub(...args);
}
dispatch(value, oldval, index) {
if(this.cache[index] != value)
this.cache[index] = value;
else
return;
if(!this.promised){
this.promised = true;
this.do_http_request().finally(() => {
this.promised = false;
});
}
}
make_on_click(...options){
let that = this;
return function(evt){
that.do_http_request(...options);
}
}
// on_click(evt, ...options) {
// this.do_http_request(...options);
// }
}
||
template "svg:*", mode="json_table_elt_render" {
error > JsonTable Widget can't contain element of type «local-name()».
}
const "hmi_textstylelists_descs", "$parsed_widgets/widget[@type = 'TextStyleList']";
const "hmi_textstylelists", "$hmi_elements[@id = $hmi_textstylelists_descs/@id]";
const "textstylelist_related" foreach "$hmi_textstylelists" list {
attrib "listid" value "@id";
foreach "func:refered_elements(.)" elt {
attrib "eltid" value "@id";
}
}
const "textstylelist_related_ns", "exsl:node-set($textstylelist_related)";
def "func:json_expressions" {
param "expressions";
param "label";
// compute javascript expressions to access JSON data
// desscribed in given svg element's "label"
// knowing that parent element already has given "expressions".
choose {
when "$label" {
const "suffixes", "str:split($label)";
const "res" foreach "$suffixes" expression {
const "suffix",".";
const "pos","position()";
// take last available expression (i.e can have more suffixes than expressions)
const "expr","$expressions[position() <= $pos][last()]/expression";
choose {
when "contains($suffix,'=')" {
const "name", "substring-before($suffix,'=')";
if "$expr/@name[. != $name]"
error > JsonTable : missplaced '=' or inconsistent names in Json data expressions.
attrib "name" value "$name";
attrib "content" > «$expr/@content»«substring-after($suffix,'=')»
}
otherwise {
copy "$expr/@name";
attrib "content" > «$expr/@content»«$suffix»
}
}
}
result "exsl:node-set($res)";
}
// Empty labels are ignored, expressions are then passed as-is.
otherwise result "$expressions";
}
}
const "initexpr" expression attrib "content" > jdata
const "initexpr_ns", "exsl:node-set($initexpr)";
template "svg:use", mode="json_table_elt_render" {
param "expressions";
// cloned element must be part of a HMI:List
const "targetid", "substring-after(@xlink:href,'#')";
const "from_list", "$hmi_lists[(@id | */@id) = $targetid]";
choose {
when "count($from_list) > 0" {
| id("«@id»").setAttribute("xlink:href",
// obtain new target id from HMI:List widget
| "#"+hmi_widgets["«$from_list/@id»"].items[«$expressions/expression[1]/@content»]);
}
otherwise
warning > Clones (svg:use) in JsonTable Widget must point to a valid HMI:List widget or item. Reference "«@xlink:href»" is not valid and will not be updated.
}
}
template "svg:text", mode="json_table_elt_render" {
param "expressions";
const "value_expr", "$expressions/expression[1]/@content";
const "original", "@original";
const "from_textstylelist", "$textstylelist_related_ns/list[elt/@eltid = $original]";
choose {
when "count($from_textstylelist) > 0" {
const "content_expr", "$expressions/expression[2]/@content";
if "string-length($content_expr) = 0 or $expressions/expression[2]/@name != 'textContent'"
error > Clones (svg:use) in JsonTable Widget pointing to a HMI:TextStyleList widget or item must have a "textContent=.someVal" assignement following value expression in label.
| {
| let elt = id("«@id»");
| elt.textContent = String(«$content_expr»);
| elt.style = hmi_widgets["«$from_textstylelist/@listid»"].styles[«$value_expr»];
| }
}
otherwise {
| id("«@id»").textContent = String(«$value_expr»);
}
}
}
// only labels comming from Json widget are counted in
def "func:filter_non_widget_label" {
param "elt";
param "widget_elts";
const "eltid" choose {
when "$elt/@original" value "$elt/@original";
otherwise value "$elt/@id";
}
result "$widget_elts[@id=$eltid]/@inkscape:label";
}
template "svg:*", mode="json_table_render_except_comments"{
param "expressions";
param "widget_elts";
const "label", "func:filter_non_widget_label(., $widget_elts)";
// filter out "# commented" elements
if "not(starts-with($label,'#'))"
apply ".", mode="json_table_render"{
with "expressions", "$expressions";
with "widget_elts", "$widget_elts";
with "label", "$label";
}
}
template "svg:*", mode="json_table_render" {
param "expressions";
param "widget_elts";
param "label";
const "new_expressions", "func:json_expressions($expressions, $label)";
const "elt",".";
foreach "$new_expressions/expression[position() > 1][starts-with(@name,'onClick')]"
| id("«$elt/@id»").onclick = this.make_on_click('«@name»', «@content»);
apply ".", mode="json_table_elt_render"
with "expressions", "$new_expressions";
}
template "svg:g", mode="json_table_render" {
param "expressions";
param "widget_elts";
param "label";
// use intermediate variables for optimization
const "varprefix" > obj_«@id»_
| try {
foreach "$expressions/expression"{
| let «$varprefix»«position()» = «@content»;
| if(«$varprefix»«position()» == undefined) {
| throw null;
| }
}
// because we put values in a variables, we can replace corresponding expression with variable name
const "new_expressions" foreach "$expressions/expression" xsl:copy {
copy "@name";
attrib "content" > «$varprefix»«position()»
}
// revert hiding in case it did happen before
| id("«@id»").style = "«@style»";
apply "*", mode="json_table_render_except_comments" {
with "expressions", "func:json_expressions(exsl:node-set($new_expressions), $label)";
with "widget_elts", "$widget_elts";
}
| } catch(err) {
| id("«@id»").style = "display:none";
| }
}
template "widget[@type='JsonTable']", mode="widget_defs" {
param "hmi_element";
labels("data");
const "data_elt", "$result_svg_ns//*[@id = $hmi_element/@id]/*[@inkscape:label = 'data']";
| visible: «count($data_elt/*[@inkscape:label])»,
| spread_json_data: function(janswer) {
| let [range,position,jdata] = janswer;
| [[1, range], [2, position], [3, this.visible]].map(([i,v]) => {
| this.apply_hmi_value(i,v);
| this.cache[i] = v;
| });
apply "$data_elt", mode="json_table_render_except_comments" {
with "expressions","$initexpr_ns";
with "widget_elts","$hmi_element/*[@inkscape:label = 'data']/descendant::svg:*";
}
| },
| init() {
| this.init_common();
foreach "$hmi_element/*[starts-with(@inkscape:label,'action_')]" {
| id("«@id»").onclick = this.make_on_click("«func:escape_quotes(@inkscape:label)»");
}
| }
}