function draw(g, state, drawInfo) {
    var x = drawInfo.texArea[0];
    var y = drawInfo.texArea[1];
    var w = drawInfo.texArea[2] - drawInfo.texArea[0];
    var h = drawInfo.texArea[3] - drawInfo.texArea[1];

    g.setColor(Color.WHITE);
    g.fillRect(drawInfo.texArea[0], drawInfo.texArea[1], drawInfo.texArea[2], drawInfo.texArea[3]);

    var routeInfo = drawInfo.routeInfo;
    // setDebugInfo(`Drawing! routeinfo: ${JSON.stringify(routeInfo)}`);
    if (routeInfo != null && routeInfo != undefined) {
        var CircularState = {
            NONE: "NONE",
            CLOCKWISE: "CLOCKWISE",
            ANTICLOCKWISE: "ANTICLOCKWISE"
        };
        var originalClip = g.getClip();
        var originalTransform = g.getTransform();
        var font = loadResource("font", "mtrsteamloco:fonts/source-han-sans.otf");
        var fontBold = loadResource("font", "mtrsteamloco:fonts/source-han-sans-bold.otf");
        var routeColor = routeInfo.routeColor;
        var arrowSide = drawInfo.arrowDirection ? Number(drawInfo.arrowDirection) : 0;
        var circularState = routeInfo.circularState;

        if (circularState == CircularState.NONE) {
            var distance = (w * 0.8) / (routeInfo.drawStations.length - 1);
            var colorGray = rgbToColor(127, 132, 137);

            g.setColor(colorGray);
            g.fillRect(widthPercent(0.1), heightPercent(0.56), w * 0.8, h * 0.08);

            if (arrowSide != 2) {
                g.fillOval(widthPercent(0.9) - h * 0.04, heightPercent(0.56), h * 0.08, h * 0.08);
                g.setColor(routeColor);
                g.fillRect(widthPercent(0.1), heightPercent(0.56), distance * (routeInfo.drawStations.length - drawInfo.index - 1), h * 0.08);
                g.fillOval(widthPercent(0.1) - h * 0.04, heightPercent(0.56), h * 0.08, h * 0.08);
            } else {
                g.fillOval(widthPercent(0.1) - h * 0.04, heightPercent(0.56), h * 0.08, h * 0.08);
                g.setColor(routeColor);
                g.fillRect(widthPercent(0.1), heightPercent(0.56), distance * (routeInfo.drawStations.length - drawInfo.index - 1), h * 0.08);
                g.fillOval(widthPercent(0.9) - h * 0.04, heightPercent(0.56), h * 0.08, h * 0.08);
            }

            for (var i = 0; i < routeInfo.drawStations.length; i++) {
                var currentX = (arrowSide != 2 ? widthPercent(0.1) : widthPercent(0.9)) + (arrowSide != 2 ? 1 : -1) * distance * i;
                var thisStn = routeInfo.drawStations[i];
                var hasPassed = i < drawInfo.index;
                var isTransfer = thisStn.transInfo.length > 0;

                if (isTransfer) {
                    g.setClip(
                        new Polygon(
                            [currentX + h * 0.025, currentX + h * 0.05, currentX - h * 0.15, currentX - h * 0.165, currentX - h * 0.165],
                            [heightPercent(0.6), heightPercent(0.6), heightPercent(0.85), heightPercent(0.85), heightPercent(0.825)],
                            5
                        )
                    );
                    for (var j = 0; j < thisStn.transInfo.length; j++) {
                        var thisX = currentX + h * 0.025 + ((h * 0.025) / thisStn.transInfo.length) * j;
                        var thisTrans = thisStn.transInfo[j];
                        g.setColor(hasPassed ? rgbToColor(124, 124, 124) : thisTrans.routeColor);
                        g.fillPolygon(
                            [thisX, thisX + (h * 0.025) / thisStn.transInfo.length + 1, thisX - h * 0.204 + (h * 0.025) / thisStn.transInfo.length + 1, thisX - h * 0.204],
                            [heightPercent(0.6), heightPercent(0.6), heightPercent(0.85), heightPercent(0.85)],
                            4
                        );
                    }
                    g.setClip(originalClip);
                }
                if (isTransfer || i == drawInfo.index) {
                    g.setColor(hasPassed ? rgbToColor(124, 124, 124) : routeColor);
                    g.fillOval(currentX - h * 0.05, heightPercent(0.55), h * 0.1, h * 0.1);
                    if (i == drawInfo.index) {
                        g.setColor(Color.RED);
                        g.fillOval(currentX - h * 0.04, heightPercent(0.56), h * 0.08, h * 0.08);

                        if (isTransfer) {
                            g.drawImage(loadResource("img", "fangsu:ris/imgtrans.png"), currentX - h * 0.04, heightPercent(0.56), h * 0.08, h * 0.08, null);
                        }
                    } else {
                        g.setColor(Color.WHITE);
                        g.fillOval(currentX - h * 0.04, heightPercent(0.56), h * 0.08, h * 0.08);
                        g.drawImage(
                            changeImageColor(loadResource("img", "fangsu:ris/imgtrans.png"), hasPassed ? rgbToColor(124, 124, 124) : routeColor),
                            currentX - h * 0.04,
                            heightPercent(0.56),
                            h * 0.08,
                            h * 0.08,
                            null
                        );
                    }
                } else {
                    g.setColor(Color.WHITE);
                    g.fillOval(currentX - h * 0.025, heightPercent(0.575), h * 0.05, h * 0.05);
                }
                var transform = AffineTransform.getRotateInstance(-0.25 * Math.PI, currentX, heightPercent(0.6));
                g.setTransform(transform);
                g.setColor(hasPassed ? rgbToColor(124, 124, 124) : Color.BLACK);
                if (i == drawInfo.index) {
                    var textLength = getDLStringWidth(g, fontBold, font, thisStn.stationName, h * 0.15);
                    g.setColor(routeColor);
                    g.fillRoundRect(currentX + h * 0.05, heightPercent(0.6) - h * 0.2, textLength + h * 0.02, h * 0.17, h * 0.01, h * 0.01);
                    g.setColor(isLightColor(routeColor) ? Color.BLACK : Color.WHITE);
                }

                drawStrDL(g, fontBold, font, thisStn.stationName, currentX + h * 0.075, heightPercent(0.6) - h * 0.2, h * 0.15, 0, 0);

                if (isTransfer) drawStrDL(g, fontBold, font, thisStn.transInfo[0].routeName, currentX - h * 0.09, heightPercent(0.6) - h * 0.025, h * 0.05, 2, 2);
                if (thisStn.transInfo.length > 1)
                    drawStrDL(g, fontBold, font, thisStn.transInfo[thisStn.transInfo.length - 1].routeName, currentX - h * 0.01, heightPercent(0.6) + h * 0.065, h * 0.05, 2, 2);
                g.setTransform(originalTransform);
            }
        }
    }

    function widthPercent(p) {
        return x + w * p;
    }
    function heightPercent(p) {
        return y + h * p;
    }
}
