org.utgenome.gwt.utgb.client.canvas.GWTGraphCanvas.java Source code

Java tutorial

Introduction

Here is the source code for org.utgenome.gwt.utgb.client.canvas.GWTGraphCanvas.java

Source

/*--------------------------------------------------------------------------
 *  Copyright 2009 utgenome.org
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *--------------------------------------------------------------------------*/
//--------------------------------------
// utgb-core Project
//
// GWTGraphCanvas.java
// Since: 2010/05/28
//
// $URL$ 
// $Author$
//--------------------------------------
package org.utgenome.gwt.utgb.client.canvas;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import org.utgenome.gwt.utgb.client.UTGBEntryPointBase;
import org.utgenome.gwt.utgb.client.bio.CompactWIGData;
import org.utgenome.gwt.utgb.client.canvas.GWTGenomeCanvas.DragPoint;
import org.utgenome.gwt.utgb.client.db.ValueDomain;
import org.utgenome.gwt.utgb.client.db.datatype.StringType;
import org.utgenome.gwt.utgb.client.track.TrackConfig;
import org.utgenome.gwt.utgb.client.track.TrackWindow;
import org.utgenome.gwt.utgb.client.util.Optional;
import org.utgenome.gwt.widget.client.Style;

import com.google.gwt.core.client.GWT;
import com.google.gwt.core.client.JavaScriptException;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.AbsolutePanel;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.widgetideas.graphics.client.Color;
import com.google.gwt.widgetideas.graphics.client.GWTCanvas;

/**
 * GWT Canvas for drawing bar graph, heat map, etc.
 * 
 * @author yoshimura
 * @author leo
 * 
 */
public class GWTGraphCanvas extends Composite {
    // widget

    private GWTCanvas frameCanvas = new GWTCanvas();
    private AbsolutePanel panel = new AbsolutePanel();
    private TrackWindow viewWindow;

    private final HashMap<TrackWindow, GraphCanvas> canvasMap = new HashMap<TrackWindow, GraphCanvas>();

    /**
     * Holder of a canvas and its corresponding track window
     * 
     * @author leo
     * 
     */
    private static class GraphCanvas {
        public TrackWindow window;
        public final List<CompactWIGData> graphData;
        public final GWTCanvas canvas = new GWTCanvas();
        public final int span;
        private int height;
        private boolean toDelete = false;

        public GraphCanvas(TrackWindow window, List<CompactWIGData> graphData, int height) {
            this.window = window;
            this.graphData = graphData;

            int maxSpan = 1;
            for (CompactWIGData each : graphData) {
                int span = each.getSpan();
                if (span > maxSpan)
                    maxSpan = span;
            }
            this.span = maxSpan;
            this.height = height;

            setPixelHeight(height);
        }

        public void setToDelete() {
            this.toDelete = true;
        }

        public boolean isToDelete() {
            return toDelete;
        }

        public void updatePixelWidth(int newPixelWidth) {
            window = window.newPixelWidthWindow(newPixelWidth);
            setPixelHeight(height);
        }

        public void clearCanvas() {
            canvas.clear();
        }

        public void setPixelHeight(int height) {
            this.height = height;

            int pixelWidthWithSpan = window.convertToPixelLength(window.getSequenceLength() + this.span - 1);

            canvas.setCoordSize(pixelWidthWithSpan, height);
            canvas.setPixelSize(pixelWidthWithSpan, height);
        }

        @Override
        public String toString() {
            return window.toString();
        }

    }

    /**
     * Graph Drawing Configurations
     * 
     * @author leo
     * 
     */
    public static class GraphStyle {
        public int windowHeight = 100;
        public float maxValue = 20.0f;
        public float minValue = 0.0f;
        public float autoScaledMax = 20.0f;
        public float autoScaledMin = 0.0f;

        public boolean autoScale = false;
        public boolean logScale = false;
        public boolean drawZeroValue = false;
        public boolean drawScale = true;
        public boolean showScaleLabel = true;
        public Optional<String> color = new Optional<String>();
        public float logBase = 2.0f;

        public final static String CONFIG_TRACK_HEIGHT = "trackHeight";
        public final static String CONFIG_GRAPH_WINDOW = "window function";
        private final static String CONFIG_MAX_VALUE = "maxValue";
        private final static String CONFIG_MIN_VALUE = "minValue";
        private final static String CONFIG_AUTO_SCALE = "autoScale";
        private final static String CONFIG_LOG_SCALE = "logScale";
        private final static String CONFIG_LOG_BASE = "log base";
        private final static String CONFIG_SHOW_ZERO_VALUE = "showZero";
        private final static String CONFIG_DRAW_SCALE = "drawScale";
        private final static String CONFIG_SHOW_SCALE_LABEL = "showScaleLabel";
        private final static String CONFIG_COLOR = "color";

        public float getDefaultMinValue() {
            return minValue;
        }

        public float getDefaultMaxValue() {
            return maxValue;
        }

        public boolean isReverseYAxis() {
            return minValue > maxValue;
        }

        /**
         * Load the parameter values from the configuration panel
         * 
         * @param config
         */
        public void load(TrackConfig config) {
            maxValue = config.getFloat(CONFIG_MAX_VALUE, maxValue);
            minValue = config.getFloat(CONFIG_MIN_VALUE, minValue);
            autoScale = config.getBoolean(CONFIG_AUTO_SCALE, autoScale);
            logScale = config.getBoolean(CONFIG_LOG_SCALE, logScale);
            logBase = config.getFloat(CONFIG_LOG_BASE, logBase);
            drawZeroValue = config.getBoolean(CONFIG_SHOW_ZERO_VALUE, drawZeroValue);
            drawScale = config.getBoolean(CONFIG_DRAW_SCALE, drawScale);
            showScaleLabel = config.getBoolean(CONFIG_SHOW_SCALE_LABEL, showScaleLabel);
            windowHeight = config.getInt(CONFIG_TRACK_HEIGHT, windowHeight);
            if (windowHeight <= 0)
                windowHeight = 100;
            String colorStr = config.getString(CONFIG_COLOR, "");
            if (colorStr != null && colorStr.length() > 0)
                color.set(colorStr);
            else
                color.reset();
        }

        /**
         * Set up the configuration panel
         * 
         * @param config
         */
        public void setup(TrackConfig config) {
            ValueDomain windowFuncitionTypes = ValueDomain
                    .createNewValueDomain(new String[] { "MAX", "MIN", "MEDIAN", "AVG" });
            config.addConfig("Window Function", new StringType(CONFIG_GRAPH_WINDOW, windowFuncitionTypes), "MAX");
            config.addConfigDouble("Y Max", CONFIG_MAX_VALUE, maxValue);
            config.addConfigDouble("Y Min", CONFIG_MIN_VALUE, minValue);
            config.addConfigBoolean("Auto Scale", CONFIG_AUTO_SCALE, autoScale);
            config.addConfigBoolean("Log Scale", CONFIG_LOG_SCALE, logScale);
            config.addConfigDouble("Log Base", CONFIG_LOG_BASE, logBase);
            config.addConfigBoolean("Show Zero Value", CONFIG_SHOW_ZERO_VALUE, drawZeroValue);
            config.addConfigBoolean("Draw Scale", CONFIG_DRAW_SCALE, drawScale);
            config.addConfigBoolean("Show Scale Label", CONFIG_SHOW_SCALE_LABEL, showScaleLabel);
            config.addConfigString("Graph Color", CONFIG_COLOR, "");
        }

    }

    private GraphStyle style = new GraphStyle();

    public GWTGraphCanvas() {

        init();
    }

    private void init() {

        Style.padding(panel, 0);
        Style.margin(panel, 0);

        panel.add(frameCanvas, 0, 0);
        //panel.add(canvas, 0, 0);
        initWidget(panel);

        sinkEvents(Event.ONMOUSEMOVE | Event.ONMOUSEOVER | Event.ONMOUSEOUT | Event.ONMOUSEDOWN | Event.ONMOUSEUP);
    }

    private Optional<DragPoint> dragStartPoint = new Optional<DragPoint>();

    @Override
    public void onBrowserEvent(Event event) {
        super.onBrowserEvent(event);

        int type = DOM.eventGetType(event);
        switch (type) {
        case Event.ONMOUSEOVER:

            break;
        case Event.ONMOUSEMOVE: {
            // show readLabels 

            if (dragStartPoint.isDefined()) {
                // scroll the canvas
                int clientX = DOM.eventGetClientX(event) + Window.getScrollLeft();
                //int clientY = DOM.eventGetClientY(event) + Window.getScrollTop();

                DragPoint p = dragStartPoint.get();
                int xDiff = clientX - p.x;
                //int yDiff = clientY - p.y;
                //panel.setWidgetPosition(canvas, xDiff, 0);
            } else {
                //Style.cursor(canvas, Style.CURSOR_AUTO);
            }

            break;
        }
        case Event.ONMOUSEOUT: {
            resetDrag(event);
            break;
        }
        case Event.ONMOUSEDOWN: {
            // invoke a click event 
            int clientX = DOM.eventGetClientX(event) + Window.getScrollLeft();
            int clientY = DOM.eventGetClientY(event) + Window.getScrollTop();

            if (dragStartPoint.isUndefined()) {
                dragStartPoint.set(new DragPoint(clientX, clientY));
                //Style.cursor(canvas, Style.CURSOR_RESIZE_E);
                event.preventDefault();
            }

            break;
        }
        case Event.ONMOUSEUP: {

            resetDrag(event);
            break;
        }
        }
    }

    private void resetDrag(Event event) {

        int clientX = DOM.eventGetClientX(event) + Window.getScrollLeft();
        //int clientY = DOM.eventGetClientY(event) + Window.getScrollTop();

        //      if (dragStartPoint.isDefined() && trackWindow != null) {
        //         DragPoint p = dragStartPoint.get();
        //         int startDiff = trackWindow.convertToGenomePosition(clientX) - trackWindow.convertToGenomePosition(p.x);
        //         if (startDiff != 0) {
        //            int newStart = trackWindow.getStartOnGenome() - startDiff;
        //            if (newStart < 1)
        //               newStart = 1;
        //            int newEnd = newStart + trackWindow.getSequenceLength();
        //            TrackWindow newWindow = trackWindow.newWindow(newStart, newEnd);
        //            if (trackGroup != null)
        //               trackGroup.setTrackWindow(newWindow);
        //         }
        //      }

        dragStartPoint.reset();

        //Style.cursor(canvas, Style.CURSOR_AUTO);
    }

    public void clear() {
        clearCanvas();
        clearScale();
    }

    public void clearCanvas() {
        for (GraphCanvas each : canvasMap.values()) {
            each.canvas.removeFromParent();
        }
        canvasMap.clear();
    }

    public void clearScale() {
        frameCanvas.clear();

        for (Label each : graphLabels) {
            each.removeFromParent();
        }
        graphLabels.clear();
    }

    /**
     * Get a canvas for a given TrackWindow
     * 
     * @param w
     * @return
     */
    private GraphCanvas getCanvas(TrackWindow w, List<CompactWIGData> data) {
        GraphCanvas graphCanvas = canvasMap.get(w);
        if (graphCanvas == null) {
            // create a new graph canvas
            graphCanvas = new GraphCanvas(w, data, style.windowHeight);
            canvasMap.put(w, graphCanvas);
            int x = viewWindow.convertToPixelX(w.getStartOnGenome());
            panel.add(graphCanvas.canvas, 0, 0);
            panel.setWidgetPosition(graphCanvas.canvas, x, 0);
        }

        return graphCanvas;
    }

    private final String DEFAULT_COLOR = "rgba(12,106,193,0.7)";

    public void redrawWigGraph() {
        for (GraphCanvas each : canvasMap.values()) {
            each.clearCanvas();
            each.setPixelHeight(style.windowHeight);
            drawWigGraph(each);
        }
    }

    public void drawWigGraph(List<CompactWIGData> data, TrackWindow w) {
        if (data == null)
            return;

        GraphCanvas canvas = getCanvas(w, data);
        drawWigGraph(canvas);
    }

    protected void drawWigGraph(GraphCanvas graphCanvas) {

        for (CompactWIGData data : graphCanvas.graphData) {

            // get graph color
            Color graphColor = new Color(DEFAULT_COLOR);
            if (style.color.isDefined()) {
                graphColor = new Color(style.color.get());
            } else if (data.getTrack().containsKey("color")) {
                String colorStr = data.getTrack().get("color");
                String c[] = colorStr.split(",");
                if (c.length == 3)
                    graphColor = new Color(Integer.valueOf(c[0]), Integer.valueOf(c[1]), Integer.valueOf(c[2]));
            }

            // draw graph
            GWTCanvas canvas = graphCanvas.canvas;

            canvas.saveContext();
            canvas.setLineWidth(1.0f);
            canvas.setStrokeStyle(graphColor);

            //canvas.scale(viewWindow.convertToPixelLength(graphCanvas.window.getSequenceLength()) / (double) data.getPixelSize(), 1.0f);

            float y2 = getYPosition(0.0f);

            // draw data graph
            final boolean isReverse = graphCanvas.window.isReverseStrand();
            final int pixelWidth = data.getData().length;

            float min = style.autoScale ? autoScaledMinValue : style.minValue;
            float max = style.autoScale ? autoScaledMaxValue : style.maxValue;

            for (int i = 0; i < pixelWidth; ++i) {
                float value = data.getData()[i];
                float y1;
                if (value == 0.0f) {
                    if (!style.drawZeroValue)
                        continue;
                    else {
                        y1 = y2 + ((min < max) ? -0.5f : 0.5f);
                    }
                } else {
                    y1 = getYPosition(value);
                }

                int x = i;
                if (isReverse) {
                    x = pixelWidth - x - 1;
                }

                canvas.saveContext();
                canvas.beginPath();
                canvas.translate(x + 0.5f, 0);
                canvas.moveTo(0, y1);
                canvas.lineTo(0, y2);
                canvas.stroke();
                canvas.restoreContext();
            }
            canvas.restoreContext();
        }

    }

    public void clearOutSideOf(TrackWindow globalWindow) {
        ArrayList<GraphCanvas> out = new ArrayList<GraphCanvas>();
        for (GraphCanvas each : canvasMap.values()) {

            if (!globalWindow.overlapWith(each.window) || each.isToDelete()) {
                out.add(each);
            }
        }
        for (GraphCanvas each : out) {
            each.canvas.clear();
            each.canvas.removeFromParent();
            canvasMap.remove(each);
        }
    }

    private List<Label> graphLabels = new ArrayList<Label>();

    public void drawFrame() {

        if (!style.drawScale)
            return;

        // draw frame
        frameCanvas.saveContext();
        frameCanvas.setStrokeStyle(new Color(0, 0, 0, 0.5f));
        frameCanvas.setLineWidth(1.0f);
        frameCanvas.beginPath();
        frameCanvas.rect(0, 0, viewWindow.getPixelWidth(), style.windowHeight);
        frameCanvas.stroke();
        frameCanvas.restoreContext();

        // draw indent line & label
        Indent indent = createIndent();

        frameCanvas.saveContext();
        frameCanvas.setStrokeStyle(Color.BLACK);
        frameCanvas.setGlobalAlpha(0.2f);
        frameCanvas.setLineWidth(0.5f);
        for (int i = 0; i <= indent.nSteps; i++) {
            float value = indent.getIndentValue(i);
            // draw indent line
            frameCanvas.saveContext();
            frameCanvas.beginPath();
            frameCanvas.translate(0, getYPosition(value) + 0.5d);
            frameCanvas.moveTo(0d, 0d);
            frameCanvas.lineTo(viewWindow.getPixelWidth(), 0);
            frameCanvas.stroke();
            frameCanvas.restoreContext();
        }
        {
            // draw zero line
            frameCanvas.saveContext();
            frameCanvas.beginPath();
            frameCanvas.translate(0, getYPosition(0f));
            frameCanvas.moveTo(0, 0);
            frameCanvas.lineTo(viewWindow.getPixelWidth(), 0);
            frameCanvas.stroke();
            frameCanvas.restoreContext();
        }

        frameCanvas.restoreContext();

    }

    public void drawScaleLabel() {

        if (!style.showScaleLabel)
            return;

        Indent indent = createIndent();
        int fontHeight = 10;

        for (int i = 0; i <= indent.nSteps; i++) {
            float value = indent.getIndentValue(i);
            String labelString = indent.getIndentString(i);
            Label label = new Label(labelString);
            label.setTitle(labelString);
            Style.fontSize(label, fontHeight);
            Style.textAlign(label, "left");
            Style.fontColor(label, "#003366");

            int labelX = 1;
            int labelY = (int) (getYPosition(value) - (fontHeight / 2.0f) - 1);

            if (labelY < 0 && labelY > -fontHeight)
                labelY = -1;

            if (labelY > style.windowHeight - fontHeight) {
                labelY = style.windowHeight - fontHeight;
            }

            //         if (labelY > style.windowHeight) {
            //            continue;
            //         }

            graphLabels.add(label);
            panel.add(label, labelX, labelY);

        }
    }

    private Indent createIndent() {
        if (style.autoScale) {
            return new Indent(autoScaledMinValue, autoScaledMaxValue, style);
        } else {
            return new Indent(style.minValue, style.maxValue, style);
        }
    }

    public static class Indent {
        public int exponent = 0;
        public long fraction = 0;

        public int nSteps = 0;

        public float min = 0.0f;
        public float max = 0.0f;

        private GraphStyle style;

        public Indent(float minValue, float maxValue, GraphStyle style) {
            this.style = style;

            final int indentHeight = 10;

            min = minValue < maxValue ? minValue : maxValue;
            max = minValue > maxValue ? minValue : maxValue;

            if (style.logScale) {
                min = getLogValue(min, style.logBase);
                max = getLogValue(max, style.logBase);
            }

            try {
                double tempIndentValue = (max - min) / style.windowHeight * indentHeight;

                if (style.logScale && tempIndentValue < 1.0)
                    tempIndentValue = 1.0;

                fraction = (long) Math.floor(Math.log10(tempIndentValue));
                exponent = (int) Math.ceil(Math.round(tempIndentValue / Math.pow(10, fraction - 3)) / 1000.0);

                if (exponent <= 5)
                    ;
                //         else if(exponent <= 7)
                //            exponent = 5;
                else {
                    exponent = 1;
                    fraction++;
                }
                double stepSize = exponent * Math.pow(10, fraction);
                max = (float) (Math.floor(max / stepSize) * stepSize);
                min = (float) (Math.ceil(min / stepSize) * stepSize);

                nSteps = (int) Math.abs((max - min) / stepSize);
            } catch (JavaScriptException e) {
                UTGBEntryPointBase.showErrorMessage(e.getMessage());
            }

        }

        public float getIndentValue(int step) {
            double indentValue = min + (step * exponent * Math.pow(10, fraction));

            if (!style.logScale)
                return (float) indentValue;
            else if (indentValue == 0.0f)
                return 0.0f;
            else if (indentValue >= 0.0f)
                return (float) Math.pow(2, indentValue - 1);
            else
                return (float) -Math.pow(2, -indentValue - 1);
        }

        public String getIndentString(int step) {
            float indentValue = getIndentValue(step);

            if (indentValue == (int) indentValue)
                return String.valueOf((int) indentValue);
            else {
                int exponent_tmp = (int) Math.ceil(Math.round(indentValue / Math.pow(10, fraction - 3)) / 1000.0);
                int endIndex = String.valueOf(exponent_tmp).length() + 1;
                if (fraction < 0)
                    endIndex -= fraction;
                endIndex = Math.min(String.valueOf(indentValue).length(), endIndex);

                return String.valueOf(indentValue).substring(0, endIndex);
            }
        }
    }

    public float getYPosition(float value) {

        float min = style.minValue;
        float max = style.maxValue;

        if (style.autoScale) {
            min = autoScaledMinValue;
            max = autoScaledMaxValue;
        }

        if (min == max)
            return 0.0f;

        float tempMin = max < min ? max : min;
        float tempMax = max > min ? max : min;

        if (style.logScale) {
            value = getLogValue(value, style.logBase);
            tempMax = getLogValue(tempMax, style.logBase);
            tempMin = getLogValue(tempMin, style.logBase);
        }
        float valueHeight = (value - tempMin) / (tempMax - tempMin) * style.windowHeight;

        if (style.isReverseYAxis())
            return valueHeight;
        else
            return style.windowHeight - valueHeight;
    }

    public static float getLogValue(float value, float logBase) {
        if (Math.log(logBase) == 0.0)
            return value;

        float temp = 0.0f;
        if (value > 0.0f) {
            temp = (float) (Math.log(value) / Math.log(logBase) + 1.0);
            if (temp < 0.0f)
                temp = 0.0f;
        } else if (value < 0.0f) {
            temp = (float) (Math.log(-value) / Math.log(logBase) + 1.0);
            if (temp < 0.0f)
                temp = 0.0f;
            temp *= -1.0f;
        }
        return temp;
    }

    private float autoScaledMinValue = 0.0f;
    private float autoScaledMaxValue = 0.0f;

    public void setViewWindow(final TrackWindow view) {

        if (viewWindow != null) {
            if (viewWindow.hasSameScaleWith(view)) {
                float tempMinValue = autoScaledMinValue;
                float tempMaxValue = autoScaledMaxValue;

                if (style.autoScale) {
                    autoScaledMinValue = 0.0f;
                    autoScaledMaxValue = 0.0f;
                }

                for (GraphCanvas each : canvasMap.values()) {
                    int start = each.window.getStartOnGenome();
                    int s = view.convertToPixelX(start);
                    panel.add(each.canvas, s, 0);
                    //panel.setWidgetPosition(each.canvas, s, 0);

                    // Auto Scale
                    if (style.autoScale) {

                        int pw = view.getPixelWidth();
                        int pw_e = each.window.getPixelWidth();

                        for (CompactWIGData wigData : each.graphData) {
                            float data[] = wigData.getData();

                            int loopStart, loopEnd;
                            if (!view.isReverseStrand()) {
                                loopStart = Math.max(-s, 0);
                                loopEnd = Math.min(pw - s, pw_e);
                            } else {
                                loopStart = Math.max(s - pw, 0);
                                loopEnd = Math.min(s, pw_e);
                            }

                            for (int pos = loopStart; pos < loopEnd; pos++) {
                                autoScaledMinValue = Math.min(autoScaledMinValue, data[pos]);
                                autoScaledMaxValue = Math.max(autoScaledMaxValue, data[pos]);
                            }
                        }
                        GWT.log("scale: " + autoScaledMinValue + " - " + autoScaledMaxValue);
                    }
                }

                if (autoScaledMinValue == autoScaledMaxValue) {
                    autoScaledMinValue = style.minValue;
                    autoScaledMaxValue = style.maxValue;
                }

                if (style.autoScale && (autoScaledMinValue != tempMinValue || autoScaledMaxValue != tempMaxValue)) {
                    redrawWigGraph();
                }

            } else {
                // zoom in/out
                for (GraphCanvas each : canvasMap.values()) {
                    int newPixelWidth = view.convertToPixelLength(each.window.getSequenceLength());
                    //each.updatePixelWidth(newPixelWidth);
                    each.setToDelete();

                    //               int start = each.window.getStartOnGenome();
                    //               int s = view.convertToPixelX(start);
                    //               panel.add(each.canvas, s, 0);
                    //               //panel.setWidgetPosition(each.canvas, s, 0);
                }
                redrawWigGraph();
            }

        }

        viewWindow = view;
    }

    public TrackWindow getViewWindow() {
        return viewWindow;
    }

    public void setStyle(GraphStyle style) {
        this.style = style;
        setPixelSize(viewWindow.getPixelWidth(), style.windowHeight);

        clearScale();
        drawFrame();
        drawScaleLabel();

        redrawWigGraph();
    }

    public GraphStyle getStyle() {
        return style;
    }

    @Override
    public void setPixelSize(int width, int height) {

        for (GraphCanvas each : canvasMap.values()) {
            each.setPixelHeight(height);
        }

        frameCanvas.setCoordSize(width, height);
        frameCanvas.setPixelWidth(width);
        frameCanvas.setPixelHeight(height);

        panel.setPixelSize(width, height);
    }

}