svghmi/widget_circularslider.ysl2
author Edouard Tisserant
Thu, 25 Mar 2021 10:13:12 +0100
branchsvghmi
changeset 3199 1582753e409b
parent 3062 9ec338a99a18
child 3232 7bdb766c2a4d
permissions -rw-r--r--
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_circuralslider.ysl2

template "widget[@type='CircularSlider']", mode="widget_class"
    ||
    class CircularSliderWidget extends Widget{
        frequency = 5;
        range = undefined;
        circle = undefined;
        handle_pos = undefined;
        curr_value = 0;
        drag = false;
        enTimer = false;
        last_drag = false;

        dispatch(value) {
            let [min,max,start,totallength] = this.range;
            //save current value inside widget
            this.curr_value = value;

            //check if in range
            if (this.curr_value > max){
                this.curr_value = max;
                this.apply_hmi_value(0, this.curr_value);
            }
            else if (this.curr_value < min){
                this.curr_value = min;
                this.apply_hmi_value(0, this.curr_value);
            }

            if(this.value_elt)
                this.value_elt.textContent = String(value);

            //don't update if draging and setpoint ghost doesn't exist
            if(!this.drag || (this.setpoint_elt != undefined)){
                this.update_DOM(value, this.handle_elt);
            }
        }

        update_DOM(value, elt){
            let [min,max,totalDistance] = this.range;
            let length = Math.max(0,Math.min((totalDistance),(Number(value)-min)/(max-min)*(totalDistance)));
            let tip = this.range_elt.getPointAtLength(length);
            elt.setAttribute('transform',"translate("+(tip.x-this.handle_pos.x)+","+(tip.y-this.handle_pos.y)+")");

            // show or hide ghost if exists
            if(this.setpoint_elt != undefined){
                if(this.last_drag!= this.drag){
                    if(this.drag){
                        this.setpoint_elt.setAttribute("style", this.setpoint_style);
                    }else{
                        this.setpoint_elt.setAttribute("style", "display:none");
                    }
                    this.last_drag = this.drag;
                }
            }
        }

        on_release(evt) {
            //unbind events
            window.removeEventListener("touchmove", this.on_bound_drag, true);
            window.removeEventListener("mousemove", this.on_bound_drag, true);

            window.removeEventListener("mouseup", this.bound_on_release, true)
            window.removeEventListener("touchend", this.bound_on_release, true);
            window.removeEventListener("touchcancel", this.bound_on_release, true);

            //reset drag flag
            if(this.drag){
                this.drag = false;
            }

            // get final position
            this.update_position(evt);
        }

        on_drag(evt){
            //ignore drag event for X amount of time and if not selected
            if(this.enTimer && this.drag){
                this.update_position(evt);

                //reset timer
                this.enTimer = false;
                setTimeout("{hmi_widgets['"+this.element_id+"'].enTimer = true;}", 100);
            }
        }

        update_position(evt){
            if(this.drag && this.enTimer){
                var svg_dist = 0;

                //calculate center of widget in html
                // --TODO maybe it would be better to bind this part to window change size event ???
                let [xdest,ydest,svgWidth,svgHeight] = page_desc[current_visible_page].bbox;
                let [cX, cY,fiStart,fiEnd,minMax,x1,y1,width,height] = this.circle;
                let htmlCirc = this.range_elt.getBoundingClientRect();
                let cxHtml = ((htmlCirc.right-htmlCirc.left)/(width)*(cX-x1))+htmlCirc.left;
                let cyHtml = ((htmlCirc.bottom-htmlCirc.top)/(height)*(cY-y1))+htmlCirc.top;


                //get mouse coordinates
                let mouseX = undefined;
                let mouseY = undefined;
                if (evt.type.startsWith("touch")){
                    mouseX = Math.ceil(evt.touches[0].clientX);
                    mouseY = Math.ceil(evt.touches[0].clientY);
                }
                else{
                    mouseX = evt.pageX;
                    mouseY = evt.pageY;
                }

                //calculate angle
                let fi = Math.atan2(cyHtml-mouseY, mouseX-cxHtml);

                // transform from 0 to 2PI
                if (fi > 0){
                    fi = 2*Math.PI-fi;
                }
                else{
                    fi = -fi;
                }

                //offset it to 0
                fi = fi - fiStart;
                if (fi < 0){
                    fi = fi + 2*Math.PI;
                }

                //get handle distance from mouse position
                if(fi<fiEnd){
                   this.curr_value=(fi)/(fiEnd)*(this.range[1]-this.range[0]);
                }
                else if(fiEnd<fi && fi<fiEnd+minMax){
                    this.curr_value = this.range[1];
                }
                else{
                    this.curr_value = this.range[0];
                }

                //apply value to hmi
                this.apply_hmi_value(0, Math.ceil(this.curr_value));

                //redraw handle
                this.request_animate();

            }

        }

        animate(){
            // redraw handle on screen refresh
            // check if setpoint(ghost) handle exsist otherwise update main handle
            if(this.setpoint_elt != undefined){
                this.update_DOM(this.curr_value, this.setpoint_elt);
            }
            else{
                this.update_DOM(this.curr_value, this.handle_elt);
            }
        }

        on_select(evt){
            //enable drag flag and timer
            this.drag = true;
            this.enTimer = true;

            //bind events
            window.addEventListener("touchmove", this.on_bound_drag, true);
            window.addEventListener("mousemove", this.on_bound_drag, true);

            window.addEventListener("mouseup", this.bound_on_release, true);
            window.addEventListener("touchend", this.bound_on_release, true);
            window.addEventListener("touchcancel", this.bound_on_release, true);

            //update postion on mouse press
            this.update_position(evt);

            //prevent next events
            evt.stopPropagation();
        }

        init() {
            //get min max
            let min = this.min_elt ?
                        Number(this.min_elt.textContent) :
                        this.args.length >= 1 ? this.args[0] : 0;
            let max = this.max_elt ?
                        Number(this.max_elt.textContent) :
                        this.args.length >= 2 ? this.args[1] : 100;

            //fiStart ==> offset
            let fiStart = Number(this.range_elt.getAttribute('sodipodi:start'));
            let fiEnd = Number(this.range_elt.getAttribute('sodipodi:end'));
            fiEnd = fiEnd - fiStart;

            //fiEnd ==> size of angle
            if (fiEnd < 0){
                fiEnd = 2*Math.PI + fiEnd;
            }

            //min max barrier angle
            let minMax = (2*Math.PI - fiEnd)/2;

            //get parameters from svg
            let cX = Number(this.range_elt.getAttribute('sodipodi:cx'));
            let cY = Number(this.range_elt.getAttribute('sodipodi:cy'));
            this.range_elt.style.strokeMiterlimit="0"; //eliminates some weird border around html object
            this.range = [min, max,this.range_elt.getTotalLength()];
            let cPos = this.range_elt.getBBox();
            this.handle_pos = this.range_elt.getPointAtLength(0);
            this.circle = [cX, cY,fiStart,fiEnd,minMax,cPos.x,cPos.y,cPos.width,cPos.height];

            //bind functions
            this.bound_on_select = this.on_select.bind(this);
            this.bound_on_release = this.on_release.bind(this);
            this.on_bound_drag = this.on_drag.bind(this);

            this.handle_elt.addEventListener("mousedown", this.bound_on_select);
            this.element.addEventListener("mousedown", this.bound_on_select);
            this.element.addEventListener("touchstart", this.bound_on_select);
            //touch recognised as page drag without next command
            document.body.addEventListener("touchstart", function(e){}, false);

            //save ghost style
            //save ghost style
            if(this.setpoint_elt != undefined){
                this.setpoint_style = this.setpoint_elt.getAttribute("style");
                this.setpoint_elt.setAttribute("style", "display:none");
            }

        }
    }
    ||

template "widget[@type='CircularSlider']", mode="widget_defs" {
    param "hmi_element";
    labels("handle range");
    optional_labels("value min max setpoint");
    |,
}