_renderExonsStructure()

in client/client/modules/render/variant-render/renderers/structural-variant-renderer.js [901:1171]


    _renderExonsStructure(viewport:BaseViewport, chromosomeName, gene, zone = 'reference', renderGeneLabels = false) {
        if (!viewport || !chromosomeName || !gene)
            return;
        chromosomeName = chromosomeName.toUpperCase();
        if (gene.empty) {
            this._renderEmptyGeneStructure(viewport, chromosomeName, zone);
            return;
        }
        const localContainer = new PIXI.Container();
        const notMaskedGraphics = new PIXI.Graphics();
        this.container.addChild(localContainer);
        this.container.addChild(notMaskedGraphics);
        const graphics = new PIXI.Graphics();
        localContainer.addChild(graphics);
        const labelContainer = new PIXI.Container();
        this.container.addChild(labelContainer);

        const geneBorder = {
            leftBorder: viewport.chromosome.start === 0,
            rightBorder: viewport.chromosome.end === gene.totalExonsLength
        };
        const height = this.variantZonesManager.getHeight(zone, chromosomeName, 'chromosome') - 2 * this.config.chromosome.margin;
        const center = this.variantZonesManager.getStartPosition(zone, chromosomeName, 'chromosome') + this.variantZonesManager.getHeight(zone, chromosomeName, 'chromosome') / 2;
        let chromosomeLineRect = null;
        const clippingRegion = {
            x: Math.round(viewport.canvas.start) - 1,
            y: Math.round(this.variantZonesManager.getStartPosition(zone, chromosomeName, 'chromosome')) - 1,
            width: Math.round(viewport.canvasSize) + 2,
            height: Math.round(this.variantZonesManager.getHeight(zone, chromosomeName, 'chromosome')) + 2
        };
        const clippingRules = {
            leftClip: false,
            rightClip: false
        };
        if (geneBorder.leftBorder && geneBorder.rightBorder) {
            clippingRules.leftClip = true;
            clippingRules.rightClip = true;
            clippingRegion.x = Math.round(viewport.canvas.start - this.config.breakpoint.width / 3) - 1;
            clippingRegion.width = Math.round(viewport.canvasSize + 2 * this.config.breakpoint.width / 3) + 2;
            chromosomeLineRect = {
                x1: viewport.canvas.start - this.config.breakpoint.width / 2,
                y1: center - height / 4,
                x2: viewport.canvas.start,
                y2: center + height / 4
            };
            graphics.beginFill(0xaaaaaa, 1);
            graphics.drawRect(
                Math.round(chromosomeLineRect.x1) - .5,
                Math.round(chromosomeLineRect.y1) - .5,
                Math.round(chromosomeLineRect.x2 - chromosomeLineRect.x1),
                Math.round(chromosomeLineRect.y2 - chromosomeLineRect.y1)
            );
            graphics.endFill();
            chromosomeLineRect = {
                x1: viewport.canvas.end,
                y1: center - height / 4,
                x2: viewport.canvas.end + this.config.breakpoint.width / 2,
                y2: center + height / 4
            };
            graphics.beginFill(0xaaaaaa, 1);
            graphics.drawRect(
                Math.round(chromosomeLineRect.x1) - .5,
                Math.round(chromosomeLineRect.y1) - .5,
                Math.round(chromosomeLineRect.x2 - chromosomeLineRect.x1),
                Math.round(chromosomeLineRect.y2 - chromosomeLineRect.y1)
            );
            graphics.endFill();
        }
        else if (geneBorder.leftBorder) {
            clippingRules.leftClip = true;
            clippingRegion.x = Math.round(viewport.canvas.start - this.config.breakpoint.width / 3) - 1;
            clippingRegion.width = Math.round(viewport.canvasSize + this.config.breakpoint.width / 3) + 2;
            chromosomeLineRect = {
                x1: viewport.canvas.start - this.config.breakpoint.width / 2,
                y1: center - height / 4,
                x2: viewport.canvas.start,
                y2: center + height / 4
            };
            graphics.beginFill(0xaaaaaa, 1);
            graphics.drawRect(
                Math.round(chromosomeLineRect.x1) - .5,
                Math.round(chromosomeLineRect.y1) - .5,
                Math.round(chromosomeLineRect.x2 - chromosomeLineRect.x1),
                Math.round(chromosomeLineRect.y2 - chromosomeLineRect.y1)
            );
            graphics.endFill();
        }
        else if (geneBorder.rightBorder) {
            clippingRules.rightClip = true;
            clippingRegion.x = Math.round(viewport.canvas.start) - 1;
            clippingRegion.width = Math.round(viewport.canvasSize + this.config.breakpoint.width / 3) + 2;
            chromosomeLineRect = {
                x1: viewport.canvas.end,
                y1: center - height / 4,
                x2: viewport.canvas.end + this.config.breakpoint.width / 2,
                y2: center + height / 4
            };
            graphics.beginFill(0xaaaaaa, 1);
            graphics.drawRect(
                Math.round(chromosomeLineRect.x1) - .5,
                Math.round(chromosomeLineRect.y1) - .5,
                Math.round(chromosomeLineRect.x2 - chromosomeLineRect.x1),
                Math.round(chromosomeLineRect.y2 - chromosomeLineRect.y1)
            );
            graphics.endFill();
        }
        let prevGeneName = null;
        for (let i = 0; i < gene.consensusExons.length; i++) {
            const exon = gene.consensusExons[i];
            if (exon.relativePosition.end < viewport.chromosome.start || exon.relativePosition.start > viewport.chromosome.end)
                continue;
            let alphaRatio = 1;
            if (this._hoveredDomain) {
                alphaRatio = .25;
                if (exon.domains && exon.domains.length > 0) {
                    for (let d = 0; d < exon.domains.length; d++) {
                        if (exon.domains[d].domain.name === this._hoveredDomain) {
                            alphaRatio = 1;
                            break;
                        }
                    }
                }
            }
            const rect = {
                x1: Math.max(viewport.project.brushBP2pixel(exon.relativePosition.start), viewport.canvas.start),
                y1: this.variantZonesManager.getStartPosition(zone, chromosomeName, 'chromosome') + this.config.chromosome.margin,
                x2: Math.min(viewport.project.brushBP2pixel(exon.relativePosition.end), viewport.canvas.end),
                y2: this.variantZonesManager.getEndPosition(zone, chromosomeName, 'chromosome') - this.config.chromosome.margin
            };
            if (exon.empty) {
                const emptyExonRect = {
                    x1: Math.max(viewport.project.brushBP2pixel(exon.relativePosition.start), viewport.canvas.start),
                    y1: center - height / 4,
                    x2: Math.min(viewport.project.brushBP2pixel(exon.relativePosition.end), viewport.canvas.end),
                    y2: center + height / 4
                };
                graphics.lineStyle(0, 0xaaaaaa, 0);
                graphics.beginFill(0xaaaaaa, 1);
                graphics.drawRect(
                    emptyExonRect.x1,
                    emptyExonRect.y1,
                    emptyExonRect.x2 - emptyExonRect.x1,
                    emptyExonRect.y2 - emptyExonRect.y1
                );
                graphics.endFill();
                continue;
            }
            if (renderGeneLabels && !exon.empty && (!prevGeneName || prevGeneName !== exon.geneName)) {
                const geneLabel = new PIXI.Text(exon.geneName, this.config.gene.label);
                geneLabel.resolution = drawingConfiguration.resolution;
                geneLabel.x = Math.round(Math.max(viewport.project.brushBP2pixel(exon.relativePosition.start), viewport.canvas.start));
                geneLabel.y = Math.round(this.variantZonesManager.getEndPosition(zone, chromosomeName, 'gene') - geneLabel.height);
                labelContainer.addChild(geneLabel);
                prevGeneName = exon.geneName;
            }
            this._registerExonPosition(exon, rect);
            const breakpointExonOffset = this.config.breakpoint.offset;
            const breakpointThickness = this.config.breakpoint.thickness;
            if (exon.domains && exon.domains.length > 0) {
                this.domainColorsManager.fillEmptyExon(exon.geneName, graphics, {
                    x: rect.x1,
                    y: rect.y1,
                    width: rect.x2 - rect.x1,
                    height: rect.y2 - rect.y1
                }, alphaRatio);
                for (let j = 0; j < exon.domains.length; j++) {
                    const exonDomain = exon.domains[j];
                    let domainAlphaRatio = 1;
                    if (this._hoveredDomain && exonDomain.domain.name !== this._hoveredDomain) {
                        domainAlphaRatio = .25;
                    }
                    const domainColor = this.domainColorsManager.getDomainColor(exonDomain.domain.name);
                    graphics.beginFill(domainColor.fill, domainColor.alpha * domainAlphaRatio);
                    graphics.lineStyle(0, 0x000000, 0);
                    const innerRect = {
                        x1: Math.max(viewport.project.brushBP2pixel(exonDomain.range.start), viewport.canvas.start),
                        y1: this.variantZonesManager.getStartPosition(zone, chromosomeName, 'chromosome') + this.config.chromosome.margin + .5,
                        x2: Math.min(viewport.project.brushBP2pixel(exonDomain.range.end), viewport.canvas.end),
                        y2: this.variantZonesManager.getEndPosition(zone, chromosomeName, 'chromosome') - this.config.chromosome.margin - .5
                    };
                    graphics.drawRect(innerRect.x1, innerRect.y1, innerRect.x2 - innerRect.x1, innerRect.y2 - innerRect.y1);
                    graphics.endFill();
                }
                graphics.beginFill(0xffffff, 0);
                graphics.lineStyle(1, 0x000000, alphaRatio);
                graphics.drawRect(Math.round(rect.x1) - .5,
                    Math.round(rect.y1) - .5,
                    Math.round(rect.x2 - rect.x1),
                    Math.round(rect.y2 - rect.y1));
                graphics.endFill();
                if (exon.isBreakpoint && this._options && this._options.highlightBreakpoints) {
                    switch (exon.breakpointPosition) {
                        case 'start':
                            notMaskedGraphics.lineStyle(breakpointThickness, this.config.breakpoint.color, 1);
                            notMaskedGraphics
                                .moveTo(Math.round(rect.x1 - breakpointExonOffset / 2) - breakpointThickness / 2, Math.round(rect.y1 - breakpointExonOffset) - breakpointThickness / 2)
                                .lineTo(Math.round(rect.x1) - breakpointThickness / 2, Math.round(rect.y1 - breakpointExonOffset) - breakpointThickness / 2)
                                .lineTo(Math.round(rect.x1) - breakpointThickness / 2, Math.round(rect.y2 + breakpointExonOffset) - breakpointThickness / 2)
                                .lineTo(Math.round(rect.x1 - breakpointExonOffset / 2) - breakpointThickness / 2, Math.round(rect.y2 + breakpointExonOffset) - breakpointThickness / 2);
                            break;
                        case 'end':
                            notMaskedGraphics.lineStyle(breakpointThickness, this.config.breakpoint.color, 1);
                            notMaskedGraphics
                                .moveTo(Math.round(rect.x2 + breakpointExonOffset / 2) - breakpointThickness / 2, Math.round(rect.y1 - breakpointExonOffset) - breakpointThickness / 2)
                                .lineTo(Math.round(rect.x2) - breakpointThickness / 2, Math.round(rect.y1 - breakpointExonOffset) - breakpointThickness / 2)
                                .lineTo(Math.round(rect.x2) - breakpointThickness / 2, Math.round(rect.y2 + breakpointExonOffset) - breakpointThickness / 2)
                                .lineTo(Math.round(rect.x2 + breakpointExonOffset / 2) - breakpointThickness / 2, Math.round(rect.y2 + breakpointExonOffset) - breakpointThickness / 2);
                            break;
                    }
                }
            }
            else {
                const rect = {
                    x1: Math.max(viewport.project.brushBP2pixel(exon.relativePosition.start), viewport.canvas.start),
                    y1: this.variantZonesManager.getStartPosition(zone, chromosomeName, 'chromosome') + this.config.chromosome.margin,
                    x2: Math.min(viewport.project.brushBP2pixel(exon.relativePosition.end), viewport.canvas.end),
                    y2: this.variantZonesManager.getEndPosition(zone, chromosomeName, 'chromosome') - this.config.chromosome.margin
                };
                this.domainColorsManager.fillEmptyExon(exon.geneName, graphics, {
                    x: rect.x1,
                    y: rect.y1,
                    width: rect.x2 - rect.x1,
                    height: rect.y2 - rect.y1
                }, alphaRatio);
                graphics.lineStyle(1, 0x000000, alphaRatio);
                graphics.drawRect(rect.x1 - .5, rect.y1 - .5, rect.x2 - rect.x1, rect.y2 - rect.y1);
                if (exon.isBreakpoint && this._options && this._options.highlightBreakpoints) {
                    switch (exon.breakpointPosition) {
                        case 'start':
                            notMaskedGraphics.lineStyle(breakpointThickness, this.config.breakpoint.color, 1);
                            notMaskedGraphics
                                .moveTo(Math.round(rect.x1 - breakpointExonOffset / 2) - breakpointThickness / 2, Math.round(rect.y1 - breakpointExonOffset) - breakpointThickness / 2)
                                .lineTo(Math.round(rect.x1) - breakpointThickness / 2, Math.round(rect.y1 - breakpointExonOffset) - breakpointThickness / 2)
                                .lineTo(Math.round(rect.x1) - breakpointThickness / 2, Math.round(rect.y2 + breakpointExonOffset) - breakpointThickness / 2)
                                .lineTo(Math.round(rect.x1 - breakpointExonOffset / 2) - breakpointThickness / 2, Math.round(rect.y2 + breakpointExonOffset) - breakpointThickness / 2);
                            break;
                        case 'end':
                            notMaskedGraphics.lineStyle(breakpointThickness, this.config.breakpoint.color, 1);
                            notMaskedGraphics
                                .moveTo(Math.round(rect.x2 + breakpointExonOffset / 2) - breakpointThickness / 2, Math.round(rect.y1 - breakpointExonOffset) - breakpointThickness / 2)
                                .lineTo(Math.round(rect.x2) - breakpointThickness / 2, Math.round(rect.y1 - breakpointExonOffset) - breakpointThickness / 2)
                                .lineTo(Math.round(rect.x2) - breakpointThickness / 2, Math.round(rect.y2 + breakpointExonOffset) - breakpointThickness / 2)
                                .lineTo(Math.round(rect.x2 + breakpointExonOffset / 2) - breakpointThickness / 2, Math.round(rect.y2 + breakpointExonOffset) - breakpointThickness / 2);
                            break;
                    }
                }
            }
            if (exon.strand !== undefined && exon.strand !== null) {
                this.strandDirectionRenderer.renderStrandDirectionForFeature(exon.strand ? 'positive' : 'negative', {
                    xStart: rect.x1,
                    xEnd: rect.x2,
                    yStart: rect.y2 - (rect.y2 - rect.y1) / 2,
                    yEnd: rect.y2
                }, localContainer, alphaRatio);
            }
            if (exon.index !== null && exon.index !== undefined) {
                const exonLabel = new PIXI.Text(exon.index + 1, this.config.exon.label);
                exonLabel.resolution = 2;
                exonLabel.alpha = alphaRatio;
                if (exonLabel.width < (rect.x2 - rect.x1)) {
                    exonLabel.x = Math.round((rect.x1 + rect.x2) / 2 - exonLabel.width / 2);
                    exonLabel.y = Math.round(rect.y1 + (rect.y2 - rect.y1) / 4 - exonLabel.height / 2);
                    localContainer.addChild(exonLabel);
                }
            }
        }
        this.featureCutOffRenderer
            .beginCuttingOff(localContainer)
            .cutOff(clippingRegion, clippingRules)
            .endCuttingOff();
    }