{"id":135,"date":"2023-05-29T20:36:46","date_gmt":"2023-05-29T12:36:46","guid":{"rendered":"https:\/\/www.kz-hub.tech\/?p=135"},"modified":"2023-05-31T15:47:20","modified_gmt":"2023-05-31T07:47:20","slug":"arriba%e7%bb%98%e5%88%b6star-fusion%e7%bb%93%e6%9e%9c","status":"publish","type":"post","link":"https:\/\/www.kz-hub.tech\/index.php\/2023\/05\/29\/arriba%e7%bb%98%e5%88%b6star-fusion%e7%bb%93%e6%9e%9c\/","title":{"rendered":"Arriba\u7ed8\u5236STAR-Fusion\u7ed3\u679c"},"content":{"rendered":"<h2>Arriba\u63d0\u4f9b\u7684draw_fusions.R<\/h2>\n<p><a href=\"https:\/\/github.com\/suhrig\/arriba\/blob\/master\/draw_fusions.R\">https:\/\/github.com\/suhrig\/arriba\/blob\/master\/draw_fusions.R<\/a><\/p>\n<pre><code class=\"language-R\">#!\/usr\/bin\/env Rscript\n\n# print warnings as they happen instead of collecting them for after a loop ends\noptions(warn=1)\n\n# define valid parameters\nparameters &lt;- list(\n    fusions=list(&quot;fusionsFile&quot;, &quot;file&quot;, &quot;fusions.tsv&quot;, T),\n    annotation=list(&quot;exonsFile&quot;, &quot;file&quot;, &quot;annotation.gtf&quot;, T),\n    output=list(&quot;outputFile&quot;, &quot;string&quot;, &quot;output.pdf&quot;, T),\n    alignments=list(&quot;alignmentsFile&quot;, &quot;file&quot;, &quot;Aligned.sortedByCoord.out.bam&quot;),\n    cytobands=list(&quot;cytobandsFile&quot;, &quot;file&quot;, &quot;cytobands.tsv&quot;),\n    minConfidenceForCircosPlot=list(&quot;minConfidenceForCircosPlot&quot;, &quot;string&quot;, &quot;medium&quot;),\n    proteinDomains=list(&quot;proteinDomainsFile&quot;, &quot;file&quot;, &quot;protein_domains.gff3&quot;),\n    sampleName=list(&quot;sampleName&quot;, &quot;string&quot;, &quot;&quot;),\n    squishIntrons=list(&quot;squishIntrons&quot;, &quot;bool&quot;, T),\n    printExonLabels=list(&quot;printExonLabels&quot;, &quot;bool&quot;, T),\n    render3dEffect=list(&quot;render3dEffect&quot;, &quot;bool&quot;, T),\n    pdfWidth=list(&quot;pdfWidth&quot;, &quot;numeric&quot;, 11.692),\n    pdfHeight=list(&quot;pdfHeight&quot;, &quot;numeric&quot;, 8.267),\n    color1=list(&quot;color1&quot;, &quot;string&quot;, &quot;#e5a5a5&quot;),\n    color2=list(&quot;color2&quot;, &quot;string&quot;, &quot;#a7c4e5&quot;),\n    mergeDomainsOverlappingBy=list(&quot;mergeDomainsOverlappingBy&quot;, &quot;numeric&quot;, 0.9),\n    optimizeDomainColors=list(&quot;optimizeDomainColors&quot;, &quot;bool&quot;, F),\n    fontSize=list(&quot;fontSize&quot;, &quot;numeric&quot;, 1),\n    fontFamily=list(&quot;fontFamily&quot;, &quot;string&quot;, &quot;Helvetica&quot;),\n    showIntergenicVicinity=list(&quot;showIntergenicVicinity&quot;, &quot;string&quot;, &quot;0&quot;),\n    transcriptSelection=list(&quot;transcriptSelection&quot;, &quot;string&quot;, &quot;provided&quot;),\n    fixedScale=list(&quot;fixedScale&quot;, &quot;numeric&quot;, 0),\n    coverageRange=list(&quot;coverageRange&quot;, &quot;string&quot;, &quot;0&quot;)\n)\n\n# print help if necessary\nargs &lt;- commandArgs(trailingOnly=T)\nif (any(grepl(&quot;^--help&quot;, args)) || length(args) == 0) {\n    usage &lt;- &quot;Usage: draw_fusions.R&quot;\n    for (parameter in names(parameters)) {\n        usage &lt;- paste0(usage, &quot; &quot;)\n        if (length(parameters[[parameter]]) &lt;= 3 || !parameters[[parameter]][[4]])\n            usage &lt;- paste0(usage, &quot;[&quot;)\n        usage &lt;- paste0(usage, &quot;--&quot;, parameter, &quot;=&quot;, parameters[[parameter]][[3]])\n        if (length(parameters[[parameter]]) &lt;= 3 || !parameters[[parameter]][[4]])\n            usage &lt;- paste0(usage, &quot;]&quot;)\n    }\n    message(usage)\n    quit(&quot;no&quot;, ifelse(length(args) == 0, 1, 0))\n}\n\n# make sure mandatory arguments are present\nfor (parameter in names(parameters))\n    if (length(parameters[[parameter]]) &gt; 3 &amp;&amp; parameters[[parameter]][[4]])\n        if (!any(grepl(paste0(&quot;^--&quot;, parameter, &quot;=&quot;), args), perl=T))\n            stop(paste0(&quot;Missing mandatory argument: --&quot;, parameter))\n\n# set default values\nfor (parameter in names(parameters))\n    assign(parameters[[parameter]][[1]], ifelse(parameters[[parameter]][[2]] == &quot;file&quot;, &quot;&quot;, parameters[[parameter]][[3]]))\n\n# parse command-line parameters\nfor (arg in args) {\n    argName &lt;- sub(&quot;=.*&quot;, &quot;&quot;, sub(&quot;^--&quot;, &quot;&quot;, arg, perl=T), perl=T)\n    argValue &lt;- sub(&quot;^[^=]*=&quot;, &quot;&quot;, arg, perl=T)\n    if (!(argName %in% names(parameters)) || !grepl(&quot;^--&quot;, arg, perl=T))\n        stop(paste(&quot;Unknown parameter:&quot;, arg))\n    if (parameters[[argName]][[2]] == &quot;bool&quot;) {\n        if (argValue %in% c(&quot;TRUE&quot;, &quot;T&quot;, &quot;FALSE&quot;, &quot;F&quot;)) {\n            assign(parameters[[argName]][[1]], as.logical(argValue))\n        } else {\n            stop(paste0(&quot;Invalid argument to --&quot;, argName))\n        }\n    } else if (parameters[[argName]][[2]] == &quot;string&quot;) {\n        assign(parameters[[argName]][[1]], argValue)\n    } else if (parameters[[argName]][[2]] == &quot;numeric&quot;) {\n        if (is.na(suppressWarnings(as.numeric(argValue))))\n            stop(paste0(&quot;Invalid argument to --&quot;, argName))\n        assign(parameters[[argName]][[1]], as.numeric(argValue))\n    } else if (parameters[[argName]][[2]] == &quot;file&quot;) {\n        if (file.access(argValue) == -1)\n            stop(paste(&quot;Cannot read file:&quot;, argValue))\n        assign(parameters[[argName]][[1]], argValue)\n    }\n}\n\n# validate values of parameters\nif (cytobandsFile == &quot;&quot;)\n    warning(&quot;Missing parameter &#039;--cytobands&#039;. No ideograms and circos plots will be drawn.&quot;)\nif (!(minConfidenceForCircosPlot %in% c(&quot;none&quot;, &quot;low&quot;, &quot;medium&quot;, &quot;high&quot;)))\n    stop(&quot;Invalid argument to --minConfidenceForCircosPlot&quot;)\nshowIntergenicVicinity &lt;- as.list(unlist(strsplit(showIntergenicVicinity, &quot;,&quot;, fixed=T)))\nif (!(length(showIntergenicVicinity) %in% c(1,4)))\n    stop(paste0(&quot;Invalid argument to --showIntergenicVicinity&quot;))\nshowIntergenicVicinity &lt;- lapply(showIntergenicVicinity, function(x) {\n    if (x == &quot;closestGene&quot;) {\n        return(&quot;exon&quot;)\n    } else if (x == &quot;closestProteinCodingGene&quot;) {\n        return(&quot;CDS&quot;)\n    } else if (is.na(suppressWarnings(as.numeric(x))) || as.numeric(x) &lt; 0) {\n        stop(paste0(&quot;Invalid argument to --showIntergenicVicinity&quot;))\n    } else {\n        return(as.numeric(x))\n    }\n})\nif (length(showIntergenicVicinity) == 1)\n    showIntergenicVicinity &lt;- rep(showIntergenicVicinity, 4)\nif (squishIntrons)\n    if (any(!is.numeric(unlist(showIntergenicVicinity))) || any(showIntergenicVicinity &gt; 0))\n        stop(&quot;--squishIntrons must be disabled, when --showIntergenicVicinity is &gt; 0&quot;)\nif (!(transcriptSelection %in% c(&quot;coverage&quot;, &quot;provided&quot;, &quot;canonical&quot;)))\n    stop(&quot;Invalid argument to --transcriptSelection&quot;)\nif (fixedScale &lt; 0)\n    stop(&quot;Invalid argument to --fixedScale&quot;)\nif (!(fontFamily %in% names(pdfFonts())))\n    stop(paste0(&quot;Unknown font: &quot;, fontFamily, &quot;. Available fonts: &quot;, paste(names(pdfFonts()), collapse=&quot;, &quot;)))\ncoverageRange &lt;- suppressWarnings(as.numeric(unlist(strsplit(coverageRange, &quot;,&quot;, fixed=T))))\nif (!(length(coverageRange) %in% 1:2) || any(is.na(coverageRange)) || any(coverageRange &lt; 0))\n    stop(&quot;Invalid argument to --coverageRange&quot;)\n\n# check if required packages are installed\nif (!suppressPackageStartupMessages(require(GenomicRanges)))\n    warning(&quot;Package &#039;GenomicRanges&#039; is not installed. No protein domains and circos plots will be drawn.&quot;)\nif (!suppressPackageStartupMessages(require(circlize)))\n    warning(&quot;Package &#039;circlize&#039; is not installed. No circos plots will be drawn.&quot;)\nif (alignmentsFile != &quot;&quot;)\n    if (!suppressPackageStartupMessages(require(GenomicAlignments)))\n        stop(&quot;Package &#039;GenomicAlignments&#039; must be installed when &#039;--alignments&#039; is used&quot;)\n\n# define colors\nchangeColorBrightness &lt;- function(color, delta) {\n    rgb(\n        min(255,max(0,col2rgb(color)[&quot;red&quot;,]+delta)),\n        min(255,max(0,col2rgb(color)[&quot;green&quot;,]+delta)),\n        min(255,max(0,col2rgb(color)[&quot;blue&quot;,]+delta)),\n        maxColorValue=255\n    )\n}\ngetDarkColor &lt;- function(color) { changeColorBrightness(color, -100) }\ngetBrightColor &lt;- function(color) { changeColorBrightness(color, +190) }\ndarkColor1 &lt;- getDarkColor(color1)\ndarkColor2 &lt;- getDarkColor(color2)\ncircosColors &lt;- c(translocation=&quot;#000000&quot;, duplication=&quot;#00bb00&quot;, deletion=&quot;#ff0000&quot;, inversion=&quot;#0000ff&quot;)\n\n# convenience functions to add\/remove &quot;chr&quot; prefix\naddChr &lt;- function(contig) {\n    ifelse(contig == &quot;MT&quot;, &quot;chrM&quot;, paste0(&quot;chr&quot;, contig))\n}\nremoveChr &lt;- function(contig) {\n    sub(&quot;^chr&quot;, &quot;&quot;, sub(&quot;^chrM&quot;, &quot;MT&quot;, contig, perl=T), perl=T)\n}\n\n# convenience function to check if a value is between two others\nbetween &lt;- function(value, start, end) {\n    value &gt;= start &amp; value &lt;= end\n}\n\n# read fusions\nfusions &lt;- read.table(fusionsFile, stringsAsFactors=F, sep=&quot;\\t&quot;, header=T, comment.char=&quot;&quot;, quote=&quot;&quot;)\nif (colnames(fusions)[1] == &quot;X.gene1&quot;) { # Arriba output\n    colnames(fusions)[colnames(fusions) %in% c(&quot;X.gene1&quot;, &quot;strand1.gene.fusion.&quot;, &quot;strand2.gene.fusion.&quot;)] &lt;- c(&quot;gene1&quot;, &quot;strand1&quot;, &quot;strand2&quot;)\n    fusions$display_contig1 &lt;- sub(&quot;:[^:]*$&quot;, &quot;&quot;, fusions$breakpoint1, perl=T)\n    fusions$display_contig2 &lt;- sub(&quot;:[^:]*$&quot;, &quot;&quot;, fusions$breakpoint2, perl=T)\n    fusions$contig1 &lt;- removeChr(fusions$display_contig1)\n    fusions$contig2 &lt;- removeChr(fusions$display_contig2)\n    fusions$breakpoint1 &lt;- as.numeric(sub(&quot;.*:&quot;, &quot;&quot;, fusions$breakpoint1, perl=T))\n    fusions$breakpoint2 &lt;- as.numeric(sub(&quot;.*:&quot;, &quot;&quot;, fusions$breakpoint2, perl=T))\n    fusions$split_reads1 &lt;- fusions$split_reads1\n    fusions$split_reads2 &lt;- fusions$split_reads2\n    fusions$type &lt;- sub(&quot;.*(translocation|duplication|deletion|inversion).*&quot;, &quot;\\\\1&quot;, fusions$type, perl=T)\n    fusions$fusion_transcript &lt;- gsub(&quot;[()^$]&quot;, &quot;&quot;, fusions$fusion_transcript)\n} else if (colnames(fusions)[1] == &quot;X.FusionName&quot;) { # STAR-Fusion\n    fusions$gene1 &lt;- sub(&quot;\\\\^.*&quot;, &quot;&quot;, fusions$LeftGene, perl=T)\n    fusions$gene2 &lt;- sub(&quot;\\\\^.*&quot;, &quot;&quot;, fusions$RightGene, perl=T)\n    fusions$strand1 &lt;- sub(&quot;.*:(.)$&quot;, &quot;\\\\1\/\\\\1&quot;, fusions$LeftBreakpoint, perl=T)\n    fusions$strand2 &lt;- sub(&quot;.*:(.)$&quot;, &quot;\\\\1\/\\\\1&quot;, fusions$RightBreakpoint, perl=T)\n    fusions$display_contig1 &lt;- sub(&quot;:[^:]*:[^:]*$&quot;, &quot;&quot;, fusions$LeftBreakpoint, perl=T)\n    fusions$display_contig2 &lt;- sub(&quot;:[^:]*:[^:]*$&quot;, &quot;&quot;, fusions$RightBreakpoint, perl=T)\n    fusions$contig1 &lt;- removeChr(fusions$display_contig1)\n    fusions$contig2 &lt;- removeChr(fusions$display_contig2)\n    fusions$breakpoint1 &lt;- as.numeric(sub(&quot;.*:([^:]*):[^:]*$&quot;, &quot;\\\\1&quot;, fusions$LeftBreakpoint, perl=T))\n    fusions$breakpoint2 &lt;- as.numeric(sub(&quot;.*:([^:]*):[^:]*$&quot;, &quot;\\\\1&quot;, fusions$RightBreakpoint, perl=T))\n    fusions$direction1 &lt;- ifelse(grepl(&quot;:\\\\+$&quot;, fusions$LeftBreakpoint, perl=T), &quot;downstream&quot;, &quot;upstream&quot;)\n    fusions$direction2 &lt;- ifelse(grepl(&quot;:\\\\+$&quot;, fusions$RightBreakpoint, perl=T), &quot;upstream&quot;, &quot;downstream&quot;)\n    fusions$gene_id1 &lt;- sub(&quot;.*\\\\^&quot;, &quot;&quot;, fusions$LeftGene, perl=T)\n    fusions$gene_id2 &lt;- sub(&quot;.*\\\\^&quot;, &quot;&quot;, fusions$RightGene, perl=T)\n    fusions$transcript_id1 &lt;- ifelse(rep(!(&quot;CDS_LEFT_ID&quot; %in% colnames(fusions)), nrow(fusions)), &quot;.&quot;, fusions$CDS_LEFT_ID)\n    fusions$transcript_id2 &lt;- ifelse(rep(!(&quot;CDS_RIGHT_ID&quot; %in% colnames(fusions)), nrow(fusions)), &quot;.&quot;, fusions$CDS_RIGHT_ID)\n    fusions$fusion_transcript &lt;- ifelse(rep(!(&quot;FUSION_CDS&quot; %in% colnames(fusions)), nrow(fusions)), &quot;.&quot;, toupper(sub(&quot;([a-z]*)&quot;, &quot;\\\\1|&quot;, fusions$FUSION_CDS, perl=T)))\n    fusions$reading_frame &lt;- ifelse(rep(!(&quot;PROT_FUSION_TYPE&quot; %in% colnames(fusions)), nrow(fusions)), &quot;.&quot;, ifelse(fusions$PROT_FUSION_TYPE == &quot;INFRAME&quot;, &quot;in-frame&quot;, ifelse(fusions$PROT_FUSION_TYPE == &quot;FRAMESHIFT&quot;, &quot;out-of-frame&quot;, &quot;.&quot;)))\n    fusions$split_reads &lt;- fusions$JunctionReadCount\n    fusions$discordant_mates &lt;- fusions$SpanningFragCount\n    fusions$site1 &lt;- rep(&quot;exon&quot;, nrow(fusions))\n    fusions$site2 &lt;- rep(&quot;exon&quot;, nrow(fusions))\n    fusions$confidence &lt;- rep(&quot;high&quot;, nrow(fusions))\n    fusions$type &lt;- ifelse(fusions$contig1 != fusions$contig2, &quot;translocation&quot;, ifelse(fusions$direction1 == fusions$direction2, &quot;inversion&quot;, ifelse((fusions$direction1 == &quot;downstream&quot;) == (fusions$breakpoint1 &lt; fusions$breakpoint2), &quot;deletion&quot;, &quot;duplication&quot;)))\n} else {\n    stop(&quot;Unrecognized fusion file format&quot;)\n}\n\npdf(outputFile, onefile=T, width=pdfWidth, height=pdfHeight, title=ifelse(sampleName != &quot;&quot;, sampleName, fusionsFile))\npar(family=fontFamily)\n\nif (nrow(fusions) == 0) {\n    plot(0, 0, type=&quot;l&quot;, xaxt=&quot;n&quot;, yaxt=&quot;n&quot;, xlab=&quot;&quot;, ylab=&quot;&quot;)\n    text(0, 0, &quot;empty input file&quot;)\n    warning(&quot;empty input file&quot;)\n    dev.off()\n    quit(&quot;no&quot;)\n}\n\n# read cytoband annotation\ncytobands &lt;- NULL\nif (cytobandsFile != &quot;&quot;) {\n    cytobands &lt;- read.table(cytobandsFile, header=T, colClasses=c(&quot;character&quot;, &quot;numeric&quot;, &quot;numeric&quot;, &quot;character&quot;, &quot;character&quot;))\n    cytobands &lt;- cytobands[order(cytobands$contig, cytobands$start, cytobands$end),]\n}\n\n# read exon annotation\nmessage(&quot;Loading annotation&quot;)\nexons &lt;- scan(exonsFile, what=list(contig=&quot;&quot;,src=&quot;&quot;,type=&quot;&quot;,start=0,end=0,score=&quot;&quot;,strand=&quot;&quot;,frame=&quot;&quot;,attributes=&quot;&quot;), sep=&quot;\\t&quot;, comment.char=&quot;#&quot;, quote=&#039;&quot;&#039;, multi.line=F)\nattr(exons, &quot;row.names&quot;) &lt;- .set_row_names(length(exons[[1]]))\nclass(exons) &lt;- &quot;data.frame&quot;\nexons &lt;- exons[exons$type %in% c(&quot;exon&quot;,&quot;CDS&quot;),c(&quot;contig&quot;,&quot;type&quot;,&quot;start&quot;,&quot;end&quot;,&quot;strand&quot;,&quot;attributes&quot;)]\nexons$contig &lt;- removeChr(exons$contig)\nparseGtfAttribute &lt;- function(attribute, gtf) {\n    parsed &lt;- sub(paste0(&quot;.*&quot;, attribute, &quot;[ =]([^;]+).*&quot;), &quot;\\\\1&quot;, gtf$attributes, perl=T)\n    failedToParse &lt;- parsed == gtf$attributes\n    if (any(failedToParse)) {\n        warning(paste0(&quot;Failed to parse &#039;&quot;, attribute, &quot;&#039; attribute of &quot;, sum(failedToParse), &quot; record(s).&quot;))\n        parsed &lt;- ifelse(failedToParse, &quot;&quot;, parsed)\n    }\n    return(parsed)\n}\nexons$geneID &lt;- parseGtfAttribute(&quot;gene_id&quot;, exons)\nexons$geneName &lt;- parseGtfAttribute(&quot;gene_name&quot;, exons)\nexons$geneName &lt;- ifelse(exons$geneName == &quot;&quot;, exons$geneID, exons$geneName)\nexons$transcript &lt;- parseGtfAttribute(&quot;transcript_id&quot;, exons)\nexons$exonNumber &lt;- ifelse(rep(printExonLabels, nrow(exons)), parseGtfAttribute(&quot;exon_number&quot;, exons), &quot;&quot;)\n\n# read protein domain annotation\nproteinDomains &lt;- NULL\nif (proteinDomainsFile != &quot;&quot;) {\n    message(&quot;Loading protein domains&quot;)\n    proteinDomains &lt;- scan(proteinDomainsFile, what=list(contig=&quot;&quot;,src=&quot;&quot;,type=&quot;&quot;,start=0,end=0,score=&quot;&quot;,strand=&quot;&quot;,frame=&quot;&quot;,attributes=&quot;&quot;), sep=&quot;\\t&quot;, comment.char=&quot;&quot;, quote=&quot;&quot;, multi.line=F)\n    attr(proteinDomains, &quot;row.names&quot;) &lt;- .set_row_names(length(proteinDomains[[1]]))\n    class(proteinDomains) &lt;- &quot;data.frame&quot;\n    proteinDomains$color &lt;- parseGtfAttribute(&quot;color&quot;, proteinDomains)\n    proteinDomains$proteinDomainName &lt;- sapply(parseGtfAttribute(&quot;Name&quot;, proteinDomains), URLdecode)\n    proteinDomains$proteinDomainID &lt;- parseGtfAttribute(&quot;protein_domain_id&quot;, proteinDomains)\n}\n\n# insert dummy annotations for intergenic breakpoints\nif (any(fusions$site1 == &quot;intergenic&quot; | fusions$site2 == &quot;intergenic&quot;)) {\n    intergenicBreakpoints &lt;- rbind(\n        setNames(fusions[fusions$site1 == &quot;intergenic&quot;,c(&quot;gene1&quot;, &quot;strand1&quot;, &quot;contig1&quot;, &quot;breakpoint1&quot;)], c(&quot;gene&quot;, &quot;strand&quot;, &quot;contig&quot;, &quot;breakpoint&quot;)),\n        setNames(fusions[fusions$site2 == &quot;intergenic&quot;,c(&quot;gene2&quot;, &quot;strand2&quot;, &quot;contig2&quot;, &quot;breakpoint2&quot;)], c(&quot;gene&quot;, &quot;strand&quot;, &quot;contig&quot;, &quot;breakpoint&quot;))\n    )\n    exons &lt;- rbind(exons, data.frame(\n        contig=intergenicBreakpoints$contig,\n        type=&quot;intergenic&quot;,\n        start=sapply(intergenicBreakpoints$breakpoint-1000, max, 1),\n        end=intergenicBreakpoints$breakpoint+1000,\n        strand=&quot;.&quot;,\n        attributes=&quot;&quot;,\n        geneName=intergenicBreakpoints$gene,\n        geneID=paste0(intergenicBreakpoints$contig, &quot;:&quot;, intergenicBreakpoints$breakpoint),\n        transcript=paste0(intergenicBreakpoints$contig, &quot;:&quot;, intergenicBreakpoints$breakpoint),\n        exonNumber=&quot;intergenic&quot;\n    ))\n    fusions[fusions$site1 == &quot;intergenic&quot;,&quot;gene_id1&quot;] &lt;- paste0(fusions[fusions$site1 == &quot;intergenic&quot;,&quot;contig1&quot;], &quot;:&quot;, fusions[fusions$site1 == &quot;intergenic&quot;,&quot;breakpoint1&quot;])\n    fusions[fusions$site2 == &quot;intergenic&quot;,&quot;gene_id2&quot;] &lt;- paste0(fusions[fusions$site2 == &quot;intergenic&quot;,&quot;contig2&quot;], &quot;:&quot;, fusions[fusions$site2 == &quot;intergenic&quot;,&quot;breakpoint2&quot;])\n}\n\ndrawVerticalGradient &lt;- function(left, right, y, color, selection=NULL) {\n    # check if gradient should only be drawn in part of the region\n    if (!is.null(selection)) {\n        y &lt;- y[selection]\n        left &lt;- left[selection]\n        right &lt;- right[selection]\n    }\n    # draw gradient\n    for (i in 1:length(y)) {\n        polygon(\n            c(left[1:i], right[1:i]),\n            c(y[1:i], y[i:1]),\n            border=NA,\n            col=rgb(col2rgb(color)[&quot;red&quot;,], col2rgb(color)[&quot;green&quot;,], col2rgb(color)[&quot;blue&quot;,], col2rgb(color, alpha=T)[&quot;alpha&quot;,]*(1\/length(y)), max=255)\n        )\n    }\n}\nif (!render3dEffect) # nullify function, if no 3D effect should be drawn\n    drawVerticalGradient &lt;- function(left, right, y, color, selection=NULL) { }\n\ndrawCurlyBrace &lt;- function(left, right, top, bottom, tip) {\n    smoothness &lt;- 20\n    x &lt;- cumsum(exp(-seq(-2.5, 2.5, len=smoothness)^2))\n    x &lt;- x\/max(x)\n    y &lt;- seq(top, bottom, len=smoothness)\n    lines(left+(tip-left)+x*(left-tip), y)\n    lines(tip+x*(right-tip), y)\n}\n\ndrawIdeogram &lt;- function(adjust, left, right, y, cytobands, contig, breakpoint) {\n    # define design of ideogram\n    bandColors &lt;- setNames(rgb(100:0, 100:0, 100:0, maxColorValue=100), paste0(&quot;gpos&quot;, 0:100))\n    bandColors &lt;- c(bandColors, gneg=&quot;#ffffff&quot;, acen=&quot;#ec4f4f&quot;, stalk=&quot;#0000ff&quot;)\n    cytobands$color &lt;- bandColors[cytobands$giemsa]\n    arcSteps &lt;- 30 # defines roundness of arc\n    curlyBraceHeight &lt;- 0.03\n    ideogramHeight &lt;- 0.04\n    ideogramWidth &lt;- 0.4\n    # extract bands of given contig\n    bands &lt;- cytobands[cytobands$contig==contig,]\n    if (nrow(bands) == 0) {\n        warning(paste(&quot;Ideogram of contig&quot;, contig, &quot;cannot be drawn, because no Giemsa staining information is available.&quot;))\n        return(NULL)\n    }\n    # scale width of ideogram to fit inside given region\n    bands$left &lt;- bands$start \/ max(cytobands$end) * ideogramWidth\n    bands$right &lt;- bands$end \/ max(cytobands$end) * ideogramWidth\n    # left\/right-align cytobands\n    offset &lt;- ifelse(adjust==&quot;left&quot;, left, right - max(bands$right))\n    bands$left &lt;- bands$left + offset\n    bands$right &lt;- bands$right + offset\n    # draw curly braces\n    tip &lt;- min(bands$left) + (max(bands$right)-min(bands$left)) \/ (max(bands$end)-min(bands$start)) * breakpoint\n    drawCurlyBrace(left, right, y-0.05+curlyBraceHeight, y-0.05, tip)\n    # draw title of chromosome\n    text((max(bands$right)+min(bands$left))\/2, y+0.07, paste(&quot;chromosome&quot;, contig), font=2, cex=fontSize, adj=c(0.5,0))\n    # draw name of band\n    bandName &lt;- bands[which(between(breakpoint, bands$start, bands$end)), &quot;name&quot;]\n    text(tip, y+0.03, bandName, cex=fontSize, adj=c(0.5,0))\n    # draw start of chromosome\n    leftArcX &lt;- bands[1,&quot;left&quot;] + (1+cos(seq(pi\/2,1.5*pi,len=arcSteps))) * (bands[1,&quot;right&quot;]-bands[1,&quot;left&quot;])\n    leftArcY &lt;- y + sin(seq(pi\/2,1.5*pi,len=arcSteps)) * (ideogramHeight\/2)\n    polygon(leftArcX, leftArcY, col=bands[1,&quot;color&quot;])\n    # draw bands\n    centromereStart &lt;- NULL\n    centromereEnd &lt;- NULL\n    for (band in 2:(nrow(bands)-1)) {\n        if (bands[band,&quot;giemsa&quot;] != &quot;acen&quot;) {\n            rect(bands[band,&quot;left&quot;], y-ideogramHeight\/2, bands[band,&quot;right&quot;], y+ideogramHeight\/2, col=bands[band,&quot;color&quot;])\n        } else { # draw centromere\n            if (is.null(centromereStart)) {\n                polygon(c(bands[band,&quot;left&quot;], bands[band,&quot;right&quot;], bands[band,&quot;left&quot;]), c(y-ideogramHeight\/2, y, y+ideogramHeight\/2), col=bands[band,&quot;color&quot;])\n                centromereStart &lt;- bands[band,&quot;left&quot;]\n            } else {\n                polygon(c(bands[band,&quot;right&quot;], bands[band,&quot;left&quot;], bands[band,&quot;right&quot;]), c(y-ideogramHeight\/2, y, y+ideogramHeight\/2), col=bands[band,&quot;color&quot;])\n                centromereEnd &lt;- bands[band,&quot;right&quot;]\n            }\n        }\n    }\n    # draw end of chromosome\n    band &lt;- nrow(bands)\n    rightArcX &lt;- bands[band,&quot;right&quot;] - (1+cos(seq(1.5*pi,pi\/2,len=arcSteps))) * (bands[band,&quot;right&quot;]-bands[band,&quot;left&quot;])\n    rightArcY &lt;- y + sin(seq(pi\/2,1.5*pi,len=arcSteps)) * ideogramHeight\/2\n    polygon(rightArcX, rightArcY, col=bands[band,&quot;color&quot;])\n    # if there is no centromere, make an artificial one with length zero\n    if (is.null(centromereStart) || is.null(centromereEnd)) {\n        centromereStart &lt;- bands[1,&quot;right&quot;]\n        centromereEnd &lt;- bands[1,&quot;right&quot;]\n    }\n    # draw gradients for 3D effect\n    drawVerticalGradient(leftArcX, rep(centromereStart, arcSteps), leftArcY, rgb(0,0,0,0.8), 1:round(arcSteps*0.4)) # black from top on p-arm\n    drawVerticalGradient(leftArcX, rep(centromereStart, arcSteps), leftArcY, rgb(1,1,1,0.7), round(arcSteps*0.4):round(arcSteps*0.1)) # white to top on p-arm\n    drawVerticalGradient(leftArcX, rep(centromereStart, arcSteps), leftArcY, rgb(1,1,1,0.7), round(arcSteps*0.4):round(arcSteps*0.6)) # white to bottom on p-arm\n    drawVerticalGradient(leftArcX, rep(centromereStart, arcSteps), leftArcY, rgb(0,0,0,0.9), arcSteps:round(arcSteps*0.5)) # black from bottom on p-arm\n    drawVerticalGradient(rightArcX, rep(centromereEnd, arcSteps), rightArcY, rgb(0,0,0,0.8), 1:round(arcSteps*0.4)) # black from top on q-arm\n    drawVerticalGradient(rightArcX, rep(centromereEnd, arcSteps), rightArcY, rgb(1,1,1,0.7), round(arcSteps*0.4):round(arcSteps*0.1)) # white to top on q-arm\n    drawVerticalGradient(rightArcX, rep(centromereEnd, arcSteps), rightArcY, rgb(1,1,1,0.7), round(arcSteps*0.4):round(arcSteps*0.6)) # white to bottom on q-arm\n    drawVerticalGradient(rightArcX, rep(centromereEnd, arcSteps), rightArcY, rgb(0,0,0,0.9), arcSteps:round(arcSteps*0.5)) # black from bottom on q-arm\n}\n\ndrawCoverage &lt;- function(left, right, y, coverage, start, end, color) {\n    maxResolution &lt;- 5000 # max number of data points to draw coverage\n    # draw coverage as bars\n    if (!is.null(coverage)) {\n        coverageData &lt;- as.numeric(coverage[IRanges(sapply(start, max, min(start(coverage))), sapply(end, min, max(end(coverage))))])\n        # downsample to maxResolution, if there are too many data points\n        coverageData &lt;- aggregate(coverageData, by=list(round(1:length(coverageData) * (right-left) * maxResolution\/length(coverageData))), mean)$x\n        polygon(c(left, seq(left, right, length.out=length(coverageData)), right), c(y, y+coverageData*0.1, y), col=color, border=NA)\n    }\n}\n\ndrawStrand &lt;- function(left, right, y, color, strand) {\n    if (strand %in% c(&quot;+&quot;, &quot;-&quot;)) {\n        # draw strand\n        lines(c(left+0.001, right-0.001), c(y, y), col=color, lwd=2)\n        lines(c(left+0.001, right-0.001), c(y, y), col=rgb(1,1,1,0.1), lwd=1)\n        # indicate orientation\n        if (right - left &gt; 0.01)\n            for (i in seq(left+0.005, right-0.005, by=sign(right-left-2*0.005)*0.01)) {\n                arrows(i, y, i+0.001*ifelse(strand==&quot;+&quot;, 1, -1), y, col=color, length=0.05, lwd=2, angle=60)\n                arrows(i, y, i+0.001*ifelse(strand==&quot;+&quot;, 1, -1), y, col=rgb(1,1,1,0.1), length=0.05, lwd=1, angle=60)\n            }\n    }\n}\n\ndrawExon &lt;- function(left, right, y, color, title, type) {\n    gradientSteps &lt;- 10 # defines smoothness of gradient\n    exonHeight &lt;- 0.03\n    if (type == &quot;CDS&quot;) {\n        # draw coding regions as thicker bars\n        rect(left, y+exonHeight, right, y+exonHeight\/2-0.001, col=color, border=NA)\n        rect(left, y-exonHeight, right, y-exonHeight\/2+0.001, col=color, border=NA)\n        # draw border\n        lines(c(left, left, right, right), c(y+exonHeight\/2, y+exonHeight, y+exonHeight, y+exonHeight\/2), col=getDarkColor(color), lend=2)\n        lines(c(left, left, right, right), c(y-exonHeight\/2, y-exonHeight, y-exonHeight, y-exonHeight\/2), col=getDarkColor(color), lend=2)\n        # draw gradients for 3D effect\n        drawVerticalGradient(rep(left, gradientSteps), rep(right, gradientSteps), seq(y+0.03, y+0.015, len=gradientSteps), rgb(0,0,0,0.2))\n        drawVerticalGradient(rep(left, gradientSteps), rep(right, gradientSteps), seq(y-0.03, y-0.015, len=gradientSteps), rgb(0,0,0,0.3))\n    } else if (type == &quot;exon&quot;) {\n        rect(left, y+exonHeight\/2, right, y-exonHeight\/2, col=color, border=getDarkColor(color))\n        # draw gradients for 3D effect\n        drawVerticalGradient(rep(left, gradientSteps), rep(right, gradientSteps), seq(y, y+exonHeight\/2, len=gradientSteps), rgb(1,1,1,0.6))\n        drawVerticalGradient(rep(left, gradientSteps), rep(right, gradientSteps), seq(y, y-exonHeight\/2, len=gradientSteps), rgb(1,1,1,0.6))\n        # add exon label\n        text((left+right)\/2, y, title, cex=0.9*fontSize)\n    }\n}\n\ndrawCircos &lt;- function(fusion, fusions, cytobands, minConfidenceForCircosPlot, circosColors) {\n    # check if Giemsa staining information is available\n    for (contig in unlist(fusions[fusion,c(&quot;contig1&quot;, &quot;contig2&quot;)])) {\n        if (!any(cytobands$contig==contig)) {\n            warning(paste0(&quot;Circos plot cannot be drawn, because no Giemsa staining information is available for contig &quot;, contig, &quot;.&quot;))\n            # draw empty plots as placeholder\n            plot(0, 0, type=&quot;l&quot;, xlim=c(0, 1), ylim=c(0, 1), bty=&quot;n&quot;, xaxt=&quot;n&quot;, yaxt=&quot;n&quot;, xlab=&quot;&quot;, ylab=&quot;&quot;)\n            plot(0, 0, type=&quot;l&quot;, xlim=c(0, 1), ylim=c(0, 1), bty=&quot;n&quot;, xaxt=&quot;n&quot;, yaxt=&quot;n&quot;, xlab=&quot;&quot;, ylab=&quot;&quot;)\n            return(NULL)\n        }\n    }\n    # initialize with empty circos plot\n    circos.clear()\n    circos.initializeWithIdeogram(cytoband=cytobands, plotType=NULL)\n    # use gene names as labels or &lt;contig&gt;:&lt;position&gt; for intergenic breakpoints\n    geneLabels &lt;- data.frame(\n        contig=c(fusions[fusion,&quot;contig1&quot;], fusions[fusion,&quot;contig2&quot;]),\n        start=c(fusions[fusion,&quot;breakpoint1&quot;], fusions[fusion,&quot;breakpoint2&quot;])\n    )\n    geneLabels$end &lt;- geneLabels$start + 1\n    geneLabels$gene &lt;- c(fusions[fusion,&quot;gene1&quot;], fusions[fusion,&quot;gene2&quot;])\n    geneLabels$gene &lt;- ifelse(c(fusions[fusion,&quot;site1&quot;], fusions[fusion,&quot;site2&quot;]) == &quot;intergenic&quot;, paste0(c(fusions[fusion,&quot;display_contig1&quot;], fusions[fusion,&quot;display_contig2&quot;]), &quot;:&quot;, geneLabels$start), geneLabels$gene)\n    # draw gene labels\n    circos.genomicLabels(geneLabels, labels.column=4, side=&quot;outside&quot;, cex=fontSize, labels_height=0.27)\n    # draw chromosome labels in connector plot\n    for (contig in unique(cytobands$contig)) {\n        set.current.cell(track.index=2, sector.index=contig) # draw in gene label connector track (track.index=2)\n        circos.text(CELL_META$xcenter, CELL_META$ycenter, contig, cex=0.85)\n    }\n    # draw ideograms\n    circos.genomicIdeogram(cytoband=cytobands)\n    # draw arcs\n    confidenceRank &lt;- c(low=0, medium=1, high=2)\n    for (i in c(setdiff(1:nrow(fusions), fusion), fusion)) { # draw fusion of interest last, such that its arc is on top\n        f &lt;- fusions[i,]\n        if (any(cytobands$contig==f$contig1) &amp;&amp; any(cytobands$contig==f$contig2)) # ignore viral contigs, because we have no cytoband information for them\n            if (minConfidenceForCircosPlot != &quot;none&quot; &amp;&amp; confidenceRank[f$confidence] &gt;= confidenceRank[minConfidenceForCircosPlot] || i==fusion)\n                circos.link(\n                    f$contig1, f$breakpoint1,\n                    f$contig2, f$breakpoint2,\n                    lwd=2, col=ifelse(i==fusion, circosColors[f$type], getBrightColor(circosColors[f$type]))\n                )\n    }\n    # draw legend\n    plot(0, 0, type=&quot;l&quot;, xlim=c(0, 1), ylim=c(0, 1), bty=&quot;n&quot;, xaxt=&quot;n&quot;, yaxt=&quot;n&quot;, ylab=&quot;&quot;, xlab=&quot;&quot;)\n    legend(x=&quot;top&quot;, legend=names(circosColors), col=sapply(circosColors, getBrightColor), lwd=3, ncol=2, box.lty=0)\n}\n\ndrawProteinDomains &lt;- function(fusion, exons1, exons2, proteinDomains, color1, color2, mergeDomainsOverlappingBy, optimizeDomainColors) {\n\n    exonHeight &lt;- 0.2\n    exonsY &lt;- 0.5\n    geneNamesY &lt;- exonsY - exonHeight\/2 - 0.05\n\n    # find coding exons\n    codingExons1 &lt;- exons1[exons1$type == &quot;CDS&quot; &amp; fusion$site1 != &quot;intergenic&quot;,]\n    codingExons2 &lt;- exons2[exons2$type == &quot;CDS&quot; &amp; fusion$site2 != &quot;intergenic&quot;,]\n\n    # cut off coding regions beyond breakpoint\n    if (fusion$direction1 == &quot;upstream&quot;) {\n        codingExons1 &lt;- codingExons1[codingExons1$end &gt;= fusion$breakpoint1,]\n        codingExons1$start &lt;- ifelse(codingExons1$start &lt; fusion$breakpoint1, fusion$breakpoint1, codingExons1$start)\n    } else {\n        codingExons1 &lt;- codingExons1[codingExons1$start &lt;= fusion$breakpoint1,]\n        codingExons1$end &lt;- ifelse(codingExons1$end &gt; fusion$breakpoint1, fusion$breakpoint1, codingExons1$end)\n    }\n    if (fusion$direction2 == &quot;upstream&quot;) {\n        codingExons2 &lt;- codingExons2[codingExons2$end &gt;= fusion$breakpoint2,]\n        codingExons2$start &lt;- ifelse(codingExons2$start &lt; fusion$breakpoint2, fusion$breakpoint2, codingExons2$start)\n    } else {\n        codingExons2 &lt;- codingExons2[codingExons2$start &lt;= fusion$breakpoint2,]\n        codingExons2$end &lt;- ifelse(codingExons2$end &gt; fusion$breakpoint2, fusion$breakpoint2, codingExons2$end)\n    }\n\n    # find overlapping domains\n    exonsGRanges1 &lt;- GRanges(codingExons1$contig, IRanges(codingExons1$start, codingExons1$end), strand=codingExons1$strand)\n    exonsGRanges2 &lt;- GRanges(codingExons2$contig, IRanges(codingExons2$start, codingExons2$end), strand=codingExons2$strand)\n    domainsGRanges &lt;- GRanges(proteinDomains$contig, IRanges(proteinDomains$start, proteinDomains$end), strand=proteinDomains$strand)\n    domainsGRanges$proteinDomainName &lt;- proteinDomains$proteinDomainName\n    domainsGRanges$proteinDomainID &lt;- proteinDomains$proteinDomainID\n    domainsGRanges$color &lt;- proteinDomains$color\n    domainsGRanges &lt;- domainsGRanges[suppressWarnings(unique(queryHits(findOverlaps(domainsGRanges, union(exonsGRanges1, exonsGRanges2)))))]\n\n    # group overlapping domains by domain ID\n    domainsGRangesList &lt;- GRangesList(lapply(unique(domainsGRanges$proteinDomainID), function(x) { domainsGRanges[domainsGRanges$proteinDomainID == x] }))\n\n    # trim protein domains to exon boundaries\n    trimDomains &lt;- function(domainsGRangesList, exonsGRanges) {\n        do.call(\n            &quot;rbind&quot;,\n            lapply(\n                domainsGRangesList,\n                function(x) {\n                    intersected &lt;- as.data.frame(reduce(suppressWarnings(intersect(x, exonsGRanges))))\n                    if (nrow(intersected) &gt; 0) {\n                        intersected$proteinDomainName &lt;- head(x$proteinDomainName, 1)\n                        intersected$proteinDomainID &lt;- head(x$proteinDomainID, 1)\n                        intersected$color &lt;- head(x$color, 1)\n                    } else {\n                        intersected$proteinDomainName &lt;- character()\n                        intersected$proteinDomainID &lt;- character()\n                        intersected$color &lt;- character()\n                    }\n                    return(intersected)\n                }\n            )\n        )\n    }\n    retainedDomains1 &lt;- trimDomains(domainsGRangesList, exonsGRanges1)\n    retainedDomains2 &lt;- trimDomains(domainsGRangesList, exonsGRanges2)\n\n    # calculate length of coding exons\n    codingExons1$length &lt;- codingExons1$end - codingExons1$start + 1\n    codingExons2$length &lt;- codingExons2$end - codingExons2$start + 1\n\n    # abort, if there are no coding regions\n    if (sum(exons1$type == &quot;CDS&quot;) + sum(exons2$type == &quot;CDS&quot;) == 0) {\n        text(0.5, 0.5, &quot;Genes are not protein-coding.&quot;)\n        return(NULL)\n    }\n    codingLength1 &lt;- sum(codingExons1$length)\n    codingLength2 &lt;- sum(codingExons2$length)\n    if (codingLength1 + codingLength2 == 0) {\n        text(0.5, 0.5, &quot;No coding regions retained in fusion transcript.&quot;)\n        return(NULL)\n    }\n    if ((codingLength1 == 0 || grepl(&quot;\\\\.$&quot;, fusion$strand1)) &amp;&amp; (codingLength2 == 0 || grepl(&quot;\\\\.$&quot;, fusion$strand2))) {\n        text(0.5, 0.5, &quot;Failed to determine retained protein domains due to lack of strand information.&quot;)\n        return(NULL)\n    }\n    antisenseTranscription1 &lt;- sub(&quot;\/.*&quot;, &quot;&quot;, fusion$strand1) != sub(&quot;.*\/&quot;, &quot;&quot;, fusion$strand1)\n    antisenseTranscription2 &lt;- sub(&quot;\/.*&quot;, &quot;&quot;, fusion$strand2) != sub(&quot;.*\/&quot;, &quot;&quot;, fusion$strand2)\n    if ((codingLength1 == 0 || antisenseTranscription1) &amp;&amp; (codingLength2 == 0 || antisenseTranscription2)) {\n        text(0.5, 0.5, &quot;No coding regions due to antisense transcription.&quot;)\n        return(NULL)\n    }\n\n    # remove introns from protein domains\n    removeIntronsFromProteinDomains &lt;- function(codingExons, retainedDomains) {\n        if (nrow(codingExons) == 0) return(NULL)\n        cumulativeIntronLength &lt;- 0\n        previousExonEnd &lt;- 0\n        for (exon in 1:nrow(codingExons)) {\n            if (codingExons[exon,&quot;start&quot;] &gt; previousExonEnd)\n                cumulativeIntronLength &lt;- cumulativeIntronLength + codingExons[exon,&quot;start&quot;] - previousExonEnd\n            domainsInExon &lt;- which(between(retainedDomains$start, codingExons[exon,&quot;start&quot;], codingExons[exon,&quot;end&quot;]))\n            retainedDomains[domainsInExon,&quot;start&quot;] &lt;- retainedDomains[domainsInExon,&quot;start&quot;] - cumulativeIntronLength\n            domainsInExon &lt;- which(between(retainedDomains$end, codingExons[exon,&quot;start&quot;], codingExons[exon,&quot;end&quot;]))\n            retainedDomains[domainsInExon,&quot;end&quot;] &lt;- retainedDomains[domainsInExon,&quot;end&quot;] - cumulativeIntronLength\n            previousExonEnd &lt;- codingExons[exon,&quot;end&quot;]\n        }\n        # merge adjacent domains\n        retainedDomains &lt;- do.call(\n            &quot;rbind&quot;,\n            lapply(\n                unique(retainedDomains$proteinDomainID),\n                function(x) {\n                    domain &lt;- retainedDomains[retainedDomains$proteinDomainID == x,]\n                    merged &lt;- reduce(GRanges(domain$seqnames, IRanges(domain$start, domain$end), strand=domain$strand))\n                    merged$proteinDomainName &lt;- head(domain$proteinDomainName, 1)\n                    merged$proteinDomainID &lt;- head(domain$proteinDomainID, 1)\n                    merged$color &lt;- head(domain$color, 1)\n                    return(as.data.frame(merged))\n                }\n            )\n        )\n        return(retainedDomains)\n    }\n    retainedDomains1 &lt;- removeIntronsFromProteinDomains(codingExons1, retainedDomains1)\n    retainedDomains2 &lt;- removeIntronsFromProteinDomains(codingExons2, retainedDomains2)\n\n    # abort, if no domains are retained\n    if (is.null(retainedDomains1) &amp;&amp; is.null(retainedDomains2)) {\n        text(0.5, 0.5, &quot;No protein domains retained in fusion.&quot;)\n        return(NULL)\n    }\n\n    # merge domains with similar coordinates\n    mergeSimilarDomains &lt;- function(domains, mergeDomainsOverlappingBy) {\n        if (is.null(domains)) return(domains)\n        merged &lt;- domains[F,] # create empty data frame\n        domains &lt;- domains[order(domains$end - domains$start, decreasing=T),] # start with bigger domains =&gt; bigger domains are retained\n        for (domain in rownames(domains)) {\n            if (!any((abs(merged$start - domains[domain,&quot;start&quot;]) + abs(merged$end - domains[domain,&quot;end&quot;])) \/ (domains[domain,&quot;end&quot;] - domains[domain,&quot;start&quot;] + 1) &lt;= 1-mergeDomainsOverlappingBy))\n                merged &lt;- rbind(merged, domains[domain,])\n        }\n        return(merged)\n    }\n    retainedDomains1 &lt;- mergeSimilarDomains(retainedDomains1, mergeDomainsOverlappingBy)\n    retainedDomains2 &lt;- mergeSimilarDomains(retainedDomains2, mergeDomainsOverlappingBy)\n\n    # if desired, reassign colors to protein domains to maximize contrast\n    if (optimizeDomainColors) {\n        uniqueDomains &lt;- unique(c(retainedDomains1$proteinDomainID, retainedDomains2$proteinDomainID))\n        # make rainbow of pretty pastell colors\n        colors &lt;- rainbow(length(uniqueDomains))\n        colors &lt;- apply(col2rgb(colors), 2, function(x) { 0.3 + y\/255 * 0.7 }) # make pastell colors\n        colors &lt;- apply(colors, 2, function(x) {rgb(x[&quot;red&quot;], x[&quot;green&quot;], x[&quot;blue&quot;])}) # convert back to rgb\n        # reassign colors\n        names(colors) &lt;- uniqueDomains\n        retainedDomains1$color &lt;- colors[retainedDomains1$proteinDomainID]\n        retainedDomains2$color &lt;- colors[retainedDomains2$proteinDomainID]\n    }\n\n    # reverse exons and protein domains, if on the reverse strand\n    if (any(codingExons1$strand == &quot;-&quot;)) {\n        codingExons1$length &lt;- rev(codingExons1$length)\n        temp &lt;- retainedDomains1$end\n        retainedDomains1$end &lt;- codingLength1 - retainedDomains1$start\n        retainedDomains1$start &lt;- codingLength1 - temp\n    }\n    if (any(codingExons2$strand == &quot;-&quot;)) {\n        codingExons2$length &lt;- rev(codingExons2$length)\n        temp &lt;- retainedDomains2$end\n        retainedDomains2$end &lt;- codingLength2 - retainedDomains2$start\n        retainedDomains2$start &lt;- codingLength2 - temp\n    }\n\n    # normalize length to 1\n    codingExons1$length &lt;- codingExons1$length \/ (codingLength1 + codingLength2)\n    codingExons2$length &lt;- codingExons2$length \/ (codingLength1 + codingLength2)\n    retainedDomains1$start &lt;- retainedDomains1$start \/ (codingLength1 + codingLength2)\n    retainedDomains1$end &lt;- retainedDomains1$end \/ (codingLength1 + codingLength2)\n    retainedDomains2$start &lt;- retainedDomains2$start \/ (codingLength1 + codingLength2)\n    retainedDomains2$end &lt;- retainedDomains2$end \/ (codingLength1 + codingLength2)\n\n    # draw coding regions\n    rect(0, exonsY-exonHeight\/2, sum(codingExons1$length), exonsY+exonHeight\/2, col=color1, border=NA)\n    rect(sum(codingExons1$length), exonsY-exonHeight\/2, sum(codingExons1$length) + sum(codingExons2$length), exonsY+exonHeight\/2, col=color2, border=NA)\n\n    # indicate exon boundaries as dotted lines\n    exonBoundaries &lt;- cumsum(c(codingExons1$length, codingExons2$length))\n    if (length(exonBoundaries) &gt; 1) {\n        exonBoundaries &lt;- exonBoundaries[1:(length(exonBoundaries)-1)]\n        for (exonBoundary in exonBoundaries)\n            lines(c(exonBoundary, exonBoundary), c(exonsY-exonHeight, exonsY+exonHeight), col=&quot;white&quot;, lty=3)\n    }\n\n    # find overlapping domains\n    # nest if one is contained in another\n    # stack if they overlap partially\n    nestDomains &lt;- function(domains) {\n        if (length(unlist(domains)) == 0) return(domains)\n        domains &lt;- domains[order(domains$end - domains$start, decreasing=T),]\n        rownames(domains) &lt;- 1:nrow(domains)\n        # find nested domains and make tree structure\n        domains$parent &lt;- 0\n        for (domain in rownames(domains))\n            domains[domains$start &gt;= domains[domain,&quot;start&quot;] &amp; domains$end &lt;= domains[domain,&quot;end&quot;] &amp; rownames(domains) != domain,&quot;parent&quot;] &lt;- domain\n        # find partially overlapping domains\n        maxOverlappingDomains &lt;- max(1, as.integer(coverage(IRanges(domains$start*10e6, domains$end*10e6))))\n        padding &lt;- 1 \/ maxOverlappingDomains * 0.4\n        domains$y &lt;- 0\n        domains$height &lt;- 0\n        adjustPositionAndHeight &lt;- function(parentDomain, y, height, padding, e) {\n            for (domain in which(e$domains$parent == parentDomain)) {\n                overlappingDomains &lt;- which((between(e$domains$start, e$domains[domain,&quot;start&quot;], e$domains[domain,&quot;end&quot;]) |\n                                             between(e$domains$end  , e$domains[domain,&quot;start&quot;], e$domains[domain,&quot;end&quot;])) &amp;\n                                             e$domains$parent == parentDomain)\n                e$domains[domain,&quot;height&quot;] &lt;- height\/length(overlappingDomains) - padding * (length(overlappingDomains)-1) \/ length(overlappingDomains)\n                e$domains[domain,&quot;y&quot;] &lt;- y + (which(domain==overlappingDomains)-1) * (e$domains[domain,&quot;height&quot;] + padding)\n                adjustPositionAndHeight(domain, e$domains[domain,&quot;y&quot;]+padding, e$domains[domain,&quot;height&quot;]-2*padding, padding, e)\n            }\n        }\n        adjustPositionAndHeight(0, 0, 1, padding, environment())\n        domains &lt;- domains[order(domains$height, decreasing=T),] # draw nested domains last\n        return(domains)\n    }\n    retainedDomains1 &lt;- nestDomains(retainedDomains1)\n    retainedDomains2 &lt;- nestDomains(retainedDomains2)\n    retainedDomains1$y &lt;- exonsY - exonHeight\/2 + 0.025 + (exonHeight-2*0.025) * retainedDomains1$y\n    retainedDomains2$y &lt;- exonsY - exonHeight\/2 + 0.025 + (exonHeight-2*0.025) * retainedDomains2$y\n    retainedDomains1$height &lt;- retainedDomains1$height * (exonHeight-2*0.025)\n    retainedDomains2$height &lt;- retainedDomains2$height * (exonHeight-2*0.025)\n\n    # draw domains\n    drawProteinDomainRect &lt;- function(left, bottom, right, top, color) {\n        rect(left, bottom, right, top, col=color, border=getDarkColor(color))\n        # draw gradients for 3D effect\n        gradientSteps &lt;- 20\n        drawVerticalGradient(rep(left, gradientSteps), rep(right, gradientSteps), seq(top, bottom, len=gradientSteps), rgb(1,1,1,0.7))\n        drawVerticalGradient(rep(left, gradientSteps), rep(right, gradientSteps), seq(bottom, bottom+(top-bottom)*0.4, len=gradientSteps), rgb(0,0,0,0.1))\n    }\n    if (length(unlist(retainedDomains1)) &gt; 0)\n        for (domain in 1:nrow(retainedDomains1))\n            drawProteinDomainRect(retainedDomains1[domain,&quot;start&quot;], retainedDomains1[domain,&quot;y&quot;], retainedDomains1[domain,&quot;end&quot;], retainedDomains1[domain,&quot;y&quot;]+retainedDomains1[domain,&quot;height&quot;], retainedDomains1[domain,&quot;color&quot;])\n    if (length(unlist(retainedDomains2)) &gt; 0)\n        for (domain in 1:nrow(retainedDomains2))\n            drawProteinDomainRect(sum(codingExons1$length)+retainedDomains2[domain,&quot;start&quot;], retainedDomains2[domain,&quot;y&quot;], sum(codingExons1$length)+retainedDomains2[domain,&quot;end&quot;], retainedDomains2[domain,&quot;y&quot;]+retainedDomains2[domain,&quot;height&quot;], retainedDomains2[domain,&quot;color&quot;])\n\n    # draw gene names, if there are coding exons\n    if (codingLength1 &gt; 0)\n        text(sum(codingExons1$length)\/2, geneNamesY, fusion$gene1, font=2, cex=fontSize)\n    if (codingLength2 &gt; 0)\n        text(sum(codingExons1$length)+sum(codingExons2$length)\/2, geneNamesY, fusion$gene2, font=2, cex=fontSize)\n\n    # calculate how many non-adjacent unique domains there are\n    # we need this info to know where to place labels vertically\n    countUniqueDomains &lt;- function(domains) {\n        uniqueDomains &lt;- 0\n        if (length(unlist(domains)) &gt; 0) {\n            uniqueDomains &lt;- 1\n            if (nrow(domains) &gt; 1) {\n                previousDomain &lt;- domains[1,&quot;proteinDomainID&quot;]\n                for (domain in 2:nrow(domains)) {\n                    if (previousDomain != domains[domain,&quot;proteinDomainID&quot;])\n                        uniqueDomains &lt;- uniqueDomains + 1\n                    previousDomain &lt;- domains[domain,&quot;proteinDomainID&quot;]\n                }\n            }\n        }\n        return(uniqueDomains)\n    }\n    if (length(unlist(retainedDomains1)) &gt; 0)\n        retainedDomains1 &lt;- retainedDomains1[order(retainedDomains1$start),]\n    uniqueDomains1 &lt;- countUniqueDomains(retainedDomains1)\n    if (length(unlist(retainedDomains2)) &gt; 0)\n        retainedDomains2 &lt;- retainedDomains2[order(retainedDomains2$end, decreasing=T),]\n    uniqueDomains2 &lt;- countUniqueDomains(retainedDomains2)\n\n    # draw title of plot\n    titleY &lt;- exonsY + exonHeight\/2 + (uniqueDomains1 + 2) * 0.05\n    text(0.5, titleY+0.01, &quot;RETAINED PROTEIN DOMAINS&quot;, adj=c(0.5, 0), font=2, cex=fontSize)\n    text(0.5, titleY, ifelse(fusion$reading_frame %in% c(&quot;in-frame&quot;, &quot;out-of-frame&quot;), paste(fusion$reading_frame, &quot;fusion&quot;), ifelse(fusion$reading_frame == &quot;stop-codon&quot;, &quot;stop codon before fusion junction&quot;, &quot;reading frame unclear&quot;)), adj=c(0.5, 1), cex=fontSize)\n\n    # draw domain labels for gene1\n    if (length(unlist(retainedDomains1)) &gt; 0) {\n        previousConnectorX &lt;- -1\n        previousLabelX &lt;- -1\n        labelY &lt;- exonsY + exonHeight\/2 + uniqueDomains1 * 0.05\n        for (domain in 1:nrow(retainedDomains1)) {\n            # if possible avoid overlapping lines of labels\n            connectorX &lt;- min(retainedDomains1[domain,&quot;start&quot;] + 0.01, (retainedDomains1[domain,&quot;start&quot;] + retainedDomains1[domain,&quot;end&quot;])\/2)\n            if (connectorX - previousConnectorX &lt; 0.01 &amp;&amp; retainedDomains1[domain,&quot;end&quot;] &gt; previousConnectorX + 0.01)\n                connectorX &lt;- previousConnectorX + 0.01\n            labelX &lt;- max(connectorX, previousLabelX) + 0.02\n            # use a signle label for adjacent domains of same type\n            adjacentDomainsOfSameType &lt;- domain + 1 &lt;= nrow(retainedDomains1) &amp;&amp; retainedDomains1[domain+1,&quot;proteinDomainID&quot;] == retainedDomains1[domain,&quot;proteinDomainID&quot;]\n            if (adjacentDomainsOfSameType) {\n                labelX &lt;- retainedDomains1[domain+1,&quot;start&quot;] + 0.015\n            } else {\n                text(labelX, labelY, retainedDomains1[domain,&quot;proteinDomainName&quot;], adj=c(0,0.5), col=getDarkColor(retainedDomains1[domain,&quot;color&quot;]), cex=fontSize)\n            }\n            lines(c(labelX-0.005, connectorX, connectorX), c(labelY, labelY, retainedDomains1[domain,&quot;y&quot;]+retainedDomains1[domain,&quot;height&quot;]), col=getDarkColor(retainedDomains1[domain,&quot;color&quot;]))\n            if (!adjacentDomainsOfSameType)\n                labelY &lt;- labelY - 0.05\n            previousConnectorX &lt;- connectorX\n            previousLabelX &lt;- labelX\n        }\n    }\n\n    # draw domain labels for gene2\n    if (length(unlist(retainedDomains2)) &gt; 0) {\n        previousConnectorX &lt;- 100\n        previousLabelX &lt;- 100\n        labelY &lt;- exonsY - exonHeight\/2 - (uniqueDomains2+1) * 0.05\n        for (domain in 1:nrow(retainedDomains2)) {\n            # if possible avoid overlapping connector lines of labels\n            connectorX &lt;- sum(codingExons1$length) + max(retainedDomains2[domain,&quot;end&quot;] - 0.01, (retainedDomains2[domain,&quot;start&quot;] + retainedDomains2[domain,&quot;end&quot;])\/2)\n            if (previousConnectorX - connectorX &lt; 0.01 &amp;&amp; sum(codingExons1$length) + retainedDomains2[domain,&quot;start&quot;] &lt; previousConnectorX - 0.01)\n                connectorX &lt;- previousConnectorX - 0.01\n            labelX &lt;- min(connectorX, previousLabelX) - 0.02\n            # use a signle label for adjacent domains of same type\n            adjacentDomainsOfSameType &lt;- domain + 1 &lt;= nrow(retainedDomains2) &amp;&amp; retainedDomains2[domain+1,&quot;proteinDomainID&quot;] == retainedDomains2[domain,&quot;proteinDomainID&quot;]\n            if (adjacentDomainsOfSameType) {\n                labelX &lt;- sum(codingExons1$length) + retainedDomains2[domain+1,&quot;end&quot;] - 0.015\n            } else {\n                text(labelX, labelY, retainedDomains2[domain,&quot;proteinDomainName&quot;], adj=c(1,0.5), col=getDarkColor(retainedDomains2[domain,&quot;color&quot;]), cex=fontSize)\n            }\n            lines(c(labelX+0.005, connectorX, connectorX), c(labelY, labelY, retainedDomains2[domain,&quot;y&quot;]), col=getDarkColor(retainedDomains2[domain,&quot;color&quot;]))\n            if (!adjacentDomainsOfSameType)\n                labelY &lt;- labelY + 0.05\n            previousConnectorX &lt;- connectorX\n            previousLabelX &lt;- labelX\n        }\n    }\n\n}\n\nfindExons &lt;- function(exons, contig, geneID, direction, breakpoint, coverage, transcriptId, transcriptSelection) {\n    # use the provided transcript if desired\n    if (transcriptSelection == &quot;provided&quot; &amp;&amp; transcriptId != &quot;.&quot; &amp;&amp; transcriptId != &quot;&quot;) {\n        candidateExons &lt;- exons[exons$transcript == transcriptId,]\n        if (nrow(candidateExons) == 0) {\n            warning(paste0(&quot;Unknown transcript given in fusions file (&quot;, transcriptId, &quot;), selecting a different one&quot;))\n        } else {\n            return(candidateExons)\n        }\n    }\n\n    if (transcriptSelection == &quot;canonical&quot;) {\n        candidateExons &lt;- exons[exons$geneID == geneID &amp; exons$contig == contig,]\n    } else {\n        # look for exon with breakpoint as splice site\n        transcripts &lt;- exons[exons$geneID == geneID &amp; exons$contig == contig &amp; exons$type == &quot;exon&quot; &amp; (direction == &quot;downstream&quot; &amp; abs(exons$end - breakpoint) &lt;= 2 | direction == &quot;upstream&quot; &amp; abs(exons$start - breakpoint) &lt;= 2),&quot;transcript&quot;]\n        candidateExons &lt;- exons[exons$transcript %in% transcripts,]\n        # if none was found, use all exons of the gene closest to the breakpoint\n        if (nrow(candidateExons) == 0)\n            candidateExons &lt;- exons[exons$geneID == geneID &amp; exons$contig == contig,]\n        # if we have coverage information, use the transcript with the highest coverage if there are multiple hits\n        if (!is.null(coverage)) {\n            highestCoverage &lt;- -1\n            transcriptWithHighestCoverage &lt;- NULL\n            lengthOfTranscriptWithHighestCoverage &lt;- 0\n            for (transcript in unique(candidateExons$transcript)) {\n                exonsOfTranscript &lt;- candidateExons[candidateExons$transcript==transcript,]\n                exonsOfTranscript$start &lt;- sapply(exonsOfTranscript$start, max, min(start(coverage)))\n                exonsOfTranscript$end &lt;- sapply(exonsOfTranscript$end, min, max(end(coverage)))\n                lengthOfTranscript &lt;- sum(exonsOfTranscript$end - exonsOfTranscript$start + 1)\n                coverageSum &lt;- sum(as.numeric(coverage[IRanges(exonsOfTranscript$start, exonsOfTranscript$end)]))\n                # we prefer shorter transcripts over longer ones, because otherwise there is a bias towards transcripts with long UTRs\n                # =&gt; a longer transcript must have substantially higher coverage to replace a shorter one\n                substantialDifference &lt;- (1 - min(lengthOfTranscript, lengthOfTranscriptWithHighestCoverage) \/ max(lengthOfTranscript, lengthOfTranscriptWithHighestCoverage)) \/ 10\n                if (lengthOfTranscript &gt; lengthOfTranscriptWithHighestCoverage &amp;&amp; coverageSum * (1-substantialDifference) &gt; highestCoverage ||\n                    lengthOfTranscript &lt; lengthOfTranscriptWithHighestCoverage &amp;&amp; coverageSum &gt; highestCoverage * (1-substantialDifference)) {\n                    highestCoverage &lt;- coverageSum\n                    transcriptWithHighestCoverage &lt;- transcript\n                    lengthOfTranscriptWithHighestCoverage &lt;- lengthOfTranscript\n                }\n            }\n            if (highestCoverage &gt; 0)\n                candidateExons &lt;- candidateExons[candidateExons$transcript==transcriptWithHighestCoverage,]\n        }\n        # if the gene has multiple transcripts, search for transcripts which encompass the breakpoint\n        if (length(unique(candidateExons$transcript)) &gt; 1) {\n            transcriptStart &lt;- aggregate(candidateExons$start, by=list(candidateExons$transcript), min)\n            rownames(transcriptStart) &lt;- transcriptStart[,1]\n            transcriptEnd &lt;- aggregate(candidateExons$end, by=list(candidateExons$transcript), max)\n            rownames(transcriptEnd) &lt;- transcriptEnd[,1]\n            encompassingExons &lt;- between(breakpoint, transcriptStart[candidateExons$transcript,2], transcriptEnd[candidateExons$transcript,2])\n            if (any(encompassingExons))\n                candidateExons &lt;- candidateExons[encompassingExons,]\n        }\n    }\n\n    # find the consensus transcript, if there are multiple hits\n    if (length(unique(candidateExons$transcript)) &gt; 1) {\n        consensusTranscript &lt;-\n            ifelse(grepl(&quot;appris_principal_1&quot;, candidateExons$attributes), 12,\n            ifelse(grepl(&quot;appris_principal_2&quot;, candidateExons$attributes), 11,\n            ifelse(grepl(&quot;appris_principal_3&quot;, candidateExons$attributes), 10,\n            ifelse(grepl(&quot;appris_principal_4&quot;, candidateExons$attributes), 9,\n            ifelse(grepl(&quot;appris_principal_5&quot;, candidateExons$attributes), 8,\n            ifelse(grepl(&quot;appris_principal&quot;, candidateExons$attributes), 7,\n            ifelse(grepl(&quot;appris_candidate_longest&quot;, candidateExons$attributes), 6,\n            ifelse(grepl(&quot;appris_candidate&quot;, candidateExons$attributes), 5,\n            ifelse(grepl(&quot;appris_alternative_1&quot;, candidateExons$attributes), 4,\n            ifelse(grepl(&quot;appris_alternative_2&quot;, candidateExons$attributes), 3,\n            ifelse(grepl(&quot;appris_alternative&quot;, candidateExons$attributes), 2,\n            ifelse(grepl(&quot;CCDS&quot;, candidateExons$attributes), 1,\n            0\n        ))))))))))))\n        candidateExons &lt;- candidateExons[consensusTranscript == max(consensusTranscript),]\n    }\n    # use the transcript with the longest coding sequence, if there are still multiple hits\n    if (length(unique(candidateExons$transcript)) &gt; 1) {\n        codingSequenceLength &lt;- ifelse(candidateExons$type == &quot;CDS&quot;, candidateExons$end - candidateExons$start, 0)\n        totalCodingSequenceLength &lt;- aggregate(codingSequenceLength, by=list(candidateExons$transcript), sum)\n        rownames(totalCodingSequenceLength) &lt;- totalCodingSequenceLength[,1]\n        candidateExons &lt;- candidateExons[totalCodingSequenceLength[candidateExons$transcript,2] == max(totalCodingSequenceLength[,2]),]\n    }\n    # use the transcript with the longest overall sequence, if there are still multiple hits\n    if (length(unique(candidateExons$transcript)) &gt; 1) {\n        exonLength &lt;- candidateExons$end - candidateExons$start\n        totalExonLength &lt;- aggregate(exonLength, by=list(candidateExons$transcript), sum)\n        rownames(totalExonLength) &lt;- totalExonLength[,1]\n        candidateExons &lt;- candidateExons[totalExonLength[candidateExons$transcript,2] == max(totalExonLength[,2]),]\n    }\n    # if there are still multiple hits, select the first one\n    candidateExons &lt;- unique(candidateExons[candidateExons$transcript == head(unique(candidateExons$transcript), 1),])\n    return(candidateExons)\n}\n\nfindClosestGene &lt;- function(exons, contig, breakpoint, extraConditions) {\n\n    # find exons near breakpoint (extraConditions must define what is considered &quot;near&quot;)\n    closestExons &lt;- exons[exons$contig == contig &amp; extraConditions,] # find closest exon\n    closestExons &lt;- exons[exons$contig == contig &amp; exons$geneID %in% closestExons$geneID,] # select all exons of closest gene\n\n    # when more than one gene found with the given name, use the closest one\n    if (length(unique(closestExons$geneID)) &gt; 1) { # more than one gene found with the given name =&gt; use the closest one\n        distanceToBreakpoint &lt;- aggregate(1:nrow(closestExons), by=list(closestExons$geneID), function(x) { min(abs(closestExons[x,&quot;start&quot;]-breakpoint), abs(closestExons[x,&quot;end&quot;]-breakpoint)) })\n        closestGene &lt;- head(distanceToBreakpoint[distanceToBreakpoint[,2] == min(distanceToBreakpoint[,2]),1], 1)\n        closestExons &lt;- closestExons[closestExons$geneID == closestGene,]\n    }\n\n    # when no gene was found, return default values\n    if (nrow(closestExons) == 0) {\n        return(IRanges(max(1, breakpoint-1000), breakpoint+1000))\n    } else {\n        return(IRanges(min(closestExons$start), max(closestExons$end)))\n    }\n}\n\n# main loop starts here\nfor (fusion in 1:nrow(fusions)) {\n\n    message(paste0(&quot;Drawing fusion #&quot;, fusion, &quot;: &quot;, fusions[fusion,&quot;gene1&quot;], &quot;:&quot;, fusions[fusion,&quot;gene2&quot;]))\n\n    # if showIntergenicVicinity is a number, take it as is\n    # if it is a keyword (closestGene\/closestProteinCodingGene), determine the range dynamically\n    showVicinity &lt;- rep(0, 4)\n    if (fusions[fusion,&quot;site1&quot;] == &quot;intergenic&quot;) {\n        showVicinity[1] &lt;- ifelse(\n            is.numeric(showIntergenicVicinity[[1]]),\n            showIntergenicVicinity[[1]],\n            fusions[fusion,&quot;breakpoint1&quot;] - start(findClosestGene(exons, fusions[fusion,&quot;contig1&quot;], fusions[fusion,&quot;breakpoint1&quot;], exons$end &lt; fusions[fusion,&quot;breakpoint1&quot;] &amp; exons$type == showIntergenicVicinity[[1]]))\n        )\n        showVicinity[2] &lt;- ifelse(\n            is.numeric(showIntergenicVicinity[[2]]),\n            showIntergenicVicinity[[2]],\n            end(findClosestGene(exons, fusions[fusion,&quot;contig1&quot;], fusions[fusion,&quot;breakpoint1&quot;], exons$start &gt; fusions[fusion,&quot;breakpoint1&quot;] &amp; exons$type == showIntergenicVicinity[[2]])) - fusions[fusion,&quot;breakpoint1&quot;]\n        )\n    }\n    if (fusions[fusion,&quot;site2&quot;] == &quot;intergenic&quot;) {\n        showVicinity[3] &lt;- ifelse(\n            is.numeric(showIntergenicVicinity[[3]]),\n            showIntergenicVicinity[[3]],\n            fusions[fusion,&quot;breakpoint2&quot;] - start(findClosestGene(exons, fusions[fusion,&quot;contig2&quot;], fusions[fusion,&quot;breakpoint2&quot;], exons$end &lt; fusions[fusion,&quot;breakpoint2&quot;] &amp; exons$type == showIntergenicVicinity[[3]]))\n        )\n        showVicinity[4] &lt;- ifelse(\n            is.numeric(showIntergenicVicinity[[4]]),\n            showIntergenicVicinity[[4]],\n            end(findClosestGene(exons, fusions[fusion,&quot;contig2&quot;], fusions[fusion,&quot;breakpoint2&quot;], exons$start &gt; fusions[fusion,&quot;breakpoint2&quot;] &amp; exons$type == showIntergenicVicinity[[4]])) - fusions[fusion,&quot;breakpoint2&quot;]\n        )\n    }\n\n    # compute coverage from alignments file\n    coverage1 &lt;- NULL\n    coverage2 &lt;- NULL\n    if (alignmentsFile != &quot;&quot;) {\n        # determine range in which we need to compute the coverage\n        determineCoverageRegion &lt;- function(exons, geneID, contig, breakpoint, showVicinityLeft, showVicinityRight) {\n            closestGene &lt;- findClosestGene(exons, contig, breakpoint, exons$geneID == geneID)\n            return(IRanges(min(start(closestGene), breakpoint-showVicinityLeft), max(end(closestGene), breakpoint+showVicinityRight)))\n        }\n        coverageRegion1 &lt;- determineCoverageRegion(exons, fusions[fusion,&quot;gene_id1&quot;], fusions[fusion,&quot;contig1&quot;], fusions[fusion,&quot;breakpoint1&quot;], showVicinity[1], showVicinity[2])\n        coverageRegion2 &lt;- determineCoverageRegion(exons, fusions[fusion,&quot;gene_id2&quot;], fusions[fusion,&quot;contig2&quot;], fusions[fusion,&quot;breakpoint2&quot;], showVicinity[3], showVicinity[4])\n        # function which reads alignments from BAM file with &amp; without &quot;chr&quot; prefix\n        readCoverage &lt;- function(alignmentsFile, contig, coverageRegion) {\n            coverageData &lt;- tryCatch(\n                {\n                    alignments &lt;- readGAlignments(alignmentsFile, param=ScanBamParam(which=GRanges(contig, coverageRegion)))\n                    coverage(alignments)[[contig]]\n                },\n                error=function(e) {\n                    alignments &lt;- readGAlignments(alignmentsFile, param=ScanBamParam(which=GRanges(addChr(contig), coverageRegion)))\n                    coverage(alignments)[[addChr(contig)]]\n                }\n            )\n            if (exists(&quot;alignments&quot;)) rm(alignments)\n            return(coverageData)\n        }\n        # get coverage track\n        coverage1 &lt;- readCoverage(alignmentsFile, fusions[fusion,&quot;contig1&quot;], coverageRegion1)\n        coverage2 &lt;- readCoverage(alignmentsFile, fusions[fusion,&quot;contig2&quot;], coverageRegion2)\n        # shrink coverage range to chromosome boundaries to avoid subscript out of bounds errors\n        coverageRegion1 &lt;- IRanges(max(start(coverageRegion1), min(start(coverage1))), min(end(coverageRegion1), max(end(coverage1))))\n        coverageRegion2 &lt;- IRanges(max(start(coverageRegion2), min(start(coverage2))), min(end(coverageRegion2), max(end(coverage2))))\n    }\n\n    # find all exons belonging to the fused genes\n    exons1 &lt;- findExons(exons, fusions[fusion,&quot;contig1&quot;], fusions[fusion,&quot;gene_id1&quot;], fusions[fusion,&quot;direction1&quot;], fusions[fusion,&quot;breakpoint1&quot;], coverage1, fusions[fusion,&quot;transcript_id1&quot;], transcriptSelection)\n    if (nrow(exons1) == 0) {\n        par(mfrow=c(1,1))\n        plot(0, 0, type=&quot;l&quot;, xaxt=&quot;n&quot;, yaxt=&quot;n&quot;, xlab=&quot;&quot;, ylab=&quot;&quot;)\n        text(0, 0, paste0(&quot;exon coordinates of &quot;, fusions[fusion,&quot;gene1&quot;], &quot; not found in\\n&quot;, exonsFile))\n        warning(paste(&quot;exon coordinates of&quot;, fusions[fusion,&quot;gene1&quot;], &quot;not found&quot;))\n        next\n    }\n    exons2 &lt;- findExons(exons, fusions[fusion,&quot;contig2&quot;], fusions[fusion,&quot;gene_id2&quot;], fusions[fusion,&quot;direction2&quot;], fusions[fusion,&quot;breakpoint2&quot;], coverage2, fusions[fusion,&quot;transcript_id2&quot;], transcriptSelection)\n    if (nrow(exons2) == 0) {\n        par(mfrow=c(1,1))\n        plot(0, 0, type=&quot;l&quot;, xaxt=&quot;n&quot;, yaxt=&quot;n&quot;, xlab=&quot;&quot;, ylab=&quot;&quot;)\n        text(0, 0, paste0(&quot;exon coordinates of &quot;, fusions[fusion,&quot;gene2&quot;], &quot; not found in\\n&quot;, exonsFile))\n        warning(paste(&quot;exon coordinates of&quot;, fusions[fusion,&quot;gene2&quot;], &quot;not found&quot;))\n        next\n    }\n\n    # in case of intergenic breakpoints, show the vicinity\n    if (sum(showVicinity) &gt; 0) {\n        if (fusions[fusion,&quot;site1&quot;] == &quot;intergenic&quot;) {\n            for (geneID in unique(exons[exons$contig == fusions[fusion,&quot;contig1&quot;] &amp; exons$exonNumber != &quot;intergenic&quot; &amp;\n                                        (between(exons$end, fusions[fusion,&quot;breakpoint1&quot;]-showVicinity[1], fusions[fusion,&quot;breakpoint1&quot;]+showVicinity[2]) |\n                                         between(exons$start, fusions[fusion,&quot;breakpoint1&quot;]-showVicinity[1], fusions[fusion,&quot;breakpoint1&quot;]+showVicinity[2])),&quot;geneID&quot;]))\n                exons1 &lt;- rbind(exons1, findExons(exons, fusions[fusion,&quot;contig1&quot;], geneID, fusions[fusion,&quot;direction1&quot;], fusions[fusion,&quot;breakpoint1&quot;], coverage1, fusions[fusion,&quot;transcript_id1&quot;], transcriptSelection))\n            # crop genes that are only partially within user-defined vicinity, because the coverage data is incomplete for those\n            exons1 &lt;- exons1[exons1$start &gt;= fusions[fusion,&quot;breakpoint1&quot;]-showVicinity[1] &amp; exons1$end &lt;= fusions[fusion,&quot;breakpoint1&quot;]+showVicinity[2] | exons1$exonNumber == &quot;intergenic&quot;,]\n        }\n        if (fusions[fusion,&quot;site2&quot;] == &quot;intergenic&quot;) {\n            for (geneID in unique(exons[exons$contig == fusions[fusion,&quot;contig2&quot;] &amp; exons$exonNumber != &quot;intergenic&quot; &amp;\n                                        (between(exons$end, fusions[fusion,&quot;breakpoint2&quot;]-showVicinity[3], fusions[fusion,&quot;breakpoint2&quot;]+showVicinity[4]) |\n                                         between(exons$start, fusions[fusion,&quot;breakpoint2&quot;]-showVicinity[3], fusions[fusion,&quot;breakpoint2&quot;]+showVicinity[4])),&quot;geneID&quot;]))\n                exons2 &lt;- rbind(exons2, findExons(exons, fusions[fusion,&quot;contig2&quot;], geneID, fusions[fusion,&quot;direction2&quot;], fusions[fusion,&quot;breakpoint2&quot;], coverage2, fusions[fusion,&quot;transcript_id2&quot;], transcriptSelection))\n            # crop genes that are only partially within user-defined vicinity, because the coverage data is incomplete for those\n            exons2 &lt;- exons2[exons2$start &gt;= fusions[fusion,&quot;breakpoint2&quot;]-showVicinity[3] &amp; exons2$end &lt;= fusions[fusion,&quot;breakpoint2&quot;]+showVicinity[4] | exons2$exonNumber == &quot;intergenic&quot;,]\n        }\n    }\n\n    # normalize coverage\n    if (alignmentsFile != &quot;&quot;) {\n        coverageNormalization &lt;- function(coverage, coverageRegion, exons) {\n            max(1, ifelse(\n                squishIntrons, # =&gt; ignore intronic coverage\n                max(as.numeric(coverage[IRanges(sapply(exons$start,max,min(start(coverage))),sapply(exons$end,min,max(end(coverage))))])),\n                round(quantile(coverage[coverageRegion], 0.9999)) # ignore coverage spikes from read-attracting regions\n            ))\n        }\n        coverageNormalization1 &lt;- ifelse(head(coverageRange,1) == 0, coverageNormalization(coverage1, coverageRegion1, exons1), head(coverageRange,1))\n        coverageNormalization2 &lt;- ifelse(tail(coverageRange,1) == 0, coverageNormalization(coverage2, coverageRegion2, exons2), tail(coverageRange,1))\n        if (length(coverageRange) == 1 &amp;&amp; coverageRange == 0) { # harmonize scales of gene1 and gene2\n            coverageNormalization1 &lt;- max(coverageNormalization1, coverageNormalization2)\n            coverageNormalization2 &lt;- max(coverageNormalization1, coverageNormalization2)\n        }\n        coverage1 &lt;- coverage1\/coverageNormalization1\n        coverage2 &lt;- coverage2\/coverageNormalization2\n        coverage1[coverage1 &gt; 1] &lt;- 1\n        coverage2[coverage2 &gt; 1] &lt;- 1\n    }\n\n    # sort coding exons last, such that they are drawn over the border of non-coding exons\n    exons1 &lt;- exons1[order(exons1$start, -rank(exons1$type)),]\n    exons2 &lt;- exons2[order(exons2$start, -rank(exons2$type)),]\n\n    # insert dummy exons, if breakpoints are outside the gene (e.g., in UTRs)\n    # this avoids plotting artifacts\n    breakpoint1 &lt;- fusions[fusion,&quot;breakpoint1&quot;]\n    breakpoint2 &lt;- fusions[fusion,&quot;breakpoint2&quot;]\n    if (breakpoint1 &lt; min(exons1$start)) {\n        exons1 &lt;- rbind(c(exons1[1,&quot;contig&quot;], &quot;dummy&quot;, max(1,breakpoint1-1000), max(1,breakpoint1-1000), exons1[1,&quot;strand&quot;], &quot;&quot;, &quot;dummy&quot;, exons1[1,&quot;geneID&quot;], exons1[1,&quot;transcript&quot;], &quot;&quot;), exons1)\n    } else if (breakpoint1 &gt; max(exons1$end)) {\n        exons1 &lt;- rbind(exons1, c(exons1[1,&quot;contig&quot;], &quot;dummy&quot;, breakpoint1+1000, breakpoint1+1000, exons1[1,&quot;strand&quot;], &quot;&quot;, &quot;dummy&quot;, exons1[1,&quot;geneID&quot;], exons1[1,&quot;transcript&quot;], &quot;&quot;))\n    }\n    if (breakpoint2 &lt; min(exons2$start)) {\n        exons2 &lt;- rbind(c(exons2[1,&quot;contig&quot;], &quot;dummy&quot;, max(1,breakpoint2-1000), max(1,breakpoint2-1000), exons2[1,&quot;strand&quot;], &quot;&quot;, &quot;dummy&quot;, exons2[1,&quot;geneID&quot;], exons2[1,&quot;transcript&quot;], &quot;&quot;), exons2)\n    } else if (breakpoint2 &gt; max(exons2$end)) {\n        exons2 &lt;- rbind(exons2, c(exons2[1,&quot;contig&quot;], &quot;dummy&quot;, breakpoint2+1000, breakpoint2+1000, exons2[1,&quot;strand&quot;], &quot;&quot;, &quot;dummy&quot;, exons2[1,&quot;geneID&quot;], exons2[1,&quot;transcript&quot;], &quot;&quot;))\n    }\n    exons1$start &lt;- as.integer(exons1$start)\n    exons1$end &lt;- as.integer(exons1$end)\n    exons2$start &lt;- as.integer(exons2$start)\n    exons2$end &lt;- as.integer(exons2$end)\n\n    exons1$left &lt;- exons1$start\n    exons1$right &lt;- exons1$end\n    exons2$left &lt;- exons2$start\n    exons2$right &lt;- exons2$end\n\n    squishedIntronSize &lt;- 200\n    if (squishIntrons) {\n        # hide introns in gene1\n        cumulativeIntronLength &lt;- 0\n        previousExonEnd &lt;- -squishedIntronSize\n        for (exon in 1:nrow(exons1)) {\n            if (breakpoint1 &gt; previousExonEnd+1 &amp;&amp; breakpoint1 &lt; exons1[exon,&quot;left&quot;])\n                breakpoint1 &lt;- (breakpoint1-previousExonEnd) \/ (exons1[exon,&quot;left&quot;]-previousExonEnd) * squishedIntronSize + previousExonEnd - cumulativeIntronLength\n            if (exons1[exon,&quot;left&quot;] &gt; previousExonEnd) {\n                cumulativeIntronLength &lt;- cumulativeIntronLength + exons1[exon,&quot;left&quot;] - previousExonEnd - squishedIntronSize\n                previousExonEnd &lt;- exons1[exon,&quot;right&quot;]\n            }\n            if (breakpoint1 &gt;= exons1[exon,&quot;left&quot;] &amp;&amp; breakpoint1 &lt;= exons1[exon,&quot;right&quot;]+1)\n                breakpoint1 &lt;- breakpoint1 - cumulativeIntronLength\n            exons1[exon,&quot;left&quot;] &lt;- exons1[exon,&quot;left&quot;] - cumulativeIntronLength\n            exons1[exon,&quot;right&quot;] &lt;- exons1[exon,&quot;right&quot;] - cumulativeIntronLength\n        }\n\n        # hide introns in gene2\n        cumulativeIntronLength &lt;- 0\n        previousExonEnd &lt;- -squishedIntronSize\n        for (exon in 1:nrow(exons2)) {\n            if (breakpoint2 &gt; previousExonEnd+1 &amp;&amp; breakpoint2 &lt; exons2[exon,&quot;left&quot;])\n                breakpoint2 &lt;- (breakpoint2-previousExonEnd) \/ (exons2[exon,&quot;left&quot;]-previousExonEnd) * squishedIntronSize + previousExonEnd - cumulativeIntronLength\n            if (exons2[exon,&quot;left&quot;] &gt; previousExonEnd) {\n                cumulativeIntronLength &lt;- cumulativeIntronLength + exons2[exon,&quot;left&quot;] - previousExonEnd - squishedIntronSize\n                previousExonEnd &lt;- exons2[exon,&quot;right&quot;]\n            }\n            if (breakpoint2 &gt;= exons2[exon,&quot;left&quot;] &amp;&amp; breakpoint2 &lt;= exons2[exon,&quot;right&quot;]+1)\n                breakpoint2 &lt;- breakpoint2 - cumulativeIntronLength\n            exons2[exon,&quot;left&quot;] &lt;- exons2[exon,&quot;left&quot;] - cumulativeIntronLength\n            exons2[exon,&quot;right&quot;] &lt;- exons2[exon,&quot;right&quot;] - cumulativeIntronLength\n        }\n    } else { # don&#039;t squish introns\n        # shift exon coordinates to align the gene to the left border of the plot\n        exons1$right &lt;- exons1$right - min(exons1$left)\n        breakpoint1 &lt;- breakpoint1 - min(exons1$left)\n        exons1$left &lt;- exons1$left - min(exons1$left)\n        exons2$right &lt;- exons2$right - min(exons2$left)\n        breakpoint2 &lt;- breakpoint2 - min(exons2$left)\n        exons2$left &lt;- exons2$left - min(exons2$left)\n    }\n\n    # scale exon sizes to fit on page\n    scalingFactor &lt;- max(exons1$right) + max(exons2$right)\n    if (fixedScale &gt; 0) {\n        if (fixedScale &gt;= scalingFactor) {\n            scalingFactor &lt;- fixedScale\n        } else {\n            warning(paste(&quot;fallback to automatic scaling, because value for --fixedScale is too small to fit transcripts on canvas (increase it to&quot;, scalingFactor, &quot;to avoid this)&quot;))\n        }\n    }\n    exons1$left &lt;- exons1$left \/ scalingFactor\n    exons1$right &lt;- exons1$right \/ scalingFactor\n    exons2$left &lt;- exons2$left \/ scalingFactor\n    exons2$right &lt;- exons2$right \/ scalingFactor\n    breakpoint1 &lt;- breakpoint1 \/ scalingFactor\n    breakpoint2 &lt;- breakpoint2 \/ scalingFactor\n\n    # shift gene2 to the right border of the page\n    gene2Offset &lt;- 1 + 0.05 - max(exons2$right)\n\n    # center fusion horizontally\n    fusionOffset1 &lt;- (max(exons1$right)+gene2Offset)\/2 - ifelse(fusions[fusion,&quot;direction1&quot;] == &quot;downstream&quot;, breakpoint1, max(exons1$right)-breakpoint1)\n    fusionOffset2 &lt;- fusionOffset1 + ifelse(fusions[fusion,&quot;direction1&quot;] == &quot;downstream&quot;, breakpoint1, max(exons1$right)-breakpoint1)\n\n    # layout: fusion on top, circos plot on bottom left, protein domains on bottom center, statistics on bottom right\n    layout(matrix(c(1,1,1,2,4,5,3,4,5), 3, 3, byrow=TRUE), widths=c(1.1, 1.2, 0.7), heights=c(1.55, 1.2, 0.25))\n    par(mar=c(0, 0, 0, 0))\n    plot(0, 0, type=&quot;l&quot;, xlim=c(-0.12, 1.12), ylim=c(0.4, 1.1), bty=&quot;n&quot;, xaxt=&quot;n&quot;, yaxt=&quot;n&quot;, xlab=&quot;&quot;, ylab=&quot;&quot;)\n\n    # vertical coordinates of layers\n    ySampleName &lt;- 1.04\n    yIdeograms &lt;- ifelse(alignmentsFile != &quot;&quot;, 0.94, 0.84)\n    yBreakpointLabels &lt;- ifelse(alignmentsFile != &quot;&quot;, 0.86, 0.76)\n    yCoverage &lt;- 0.72\n    yExons &lt;- 0.67\n    yGeneNames &lt;- 0.58\n    yFusion &lt;- 0.5\n    yTranscript &lt;- 0.45\n    yScale &lt;- 0.407\n    yTrajectoryBreakpointLabels &lt;- yBreakpointLabels - 0.035\n    yTrajectoryExonTop &lt;- yExons + 0.03\n    yTrajectoryExonBottom &lt;- yExons - 0.055\n    yTrajectoryFusion &lt;- yFusion + 0.03\n\n    # print sample name (title of page)\n    text(0.5, ySampleName, sampleName, font=2, cex=fontSize*1.5, adj=c(0.5,0))\n\n    # draw ideograms\n    if (!is.null(cytobands)) {\n        drawIdeogram(&quot;left&quot;, min(exons1$left), max(exons1$right), yIdeograms, cytobands, fusions[fusion,&quot;contig1&quot;], fusions[fusion,&quot;breakpoint1&quot;])\n        drawIdeogram(&quot;right&quot;, gene2Offset, gene2Offset+max(exons2$right), yIdeograms, cytobands, fusions[fusion,&quot;contig2&quot;], fusions[fusion,&quot;breakpoint2&quot;])\n    }\n\n    # draw gene &amp; transcript names\n    if (fusions[fusion,&quot;gene1&quot;] != &quot;.&quot;)\n        text(max(exons1$right)\/2, yGeneNames, fusions[fusion,&quot;gene1&quot;], font=2, cex=fontSize, adj=c(0.5,0))\n    if (fusions[fusion,&quot;site1&quot;] != &quot;intergenic&quot;)\n        text(max(exons1$right)\/2, yGeneNames-0.01, head(exons1$transcript,1), cex=0.9*fontSize, adj=c(0.5,1))\n    if (fusions[fusion,&quot;gene2&quot;] != &quot;.&quot;)\n        text(gene2Offset+max(exons2$right)\/2, yGeneNames, fusions[fusion,&quot;gene2&quot;], font=2, cex=fontSize, adj=c(0.5,0))\n    if (fusions[fusion,&quot;site2&quot;] != &quot;intergenic&quot;)\n        text(gene2Offset+max(exons2$right)\/2, yGeneNames-0.01, head(exons2$transcript,1), cex=0.9*fontSize, adj=c(0.5,1))\n\n    # if multiple genes in the vicinity are shown, label them\n    if (fusions[fusion,&quot;site1&quot;] == &quot;intergenic&quot;)\n        for (gene in unique(exons1$geneName)) {\n            exonsOfGene &lt;- exons1[exons1$geneName == gene &amp; exons1$type != &quot;dummy&quot;,]\n            if (any(exonsOfGene$type == &quot;exon&quot;))\n                text(mean(c(min(exonsOfGene$left), max(exonsOfGene$right))), yExons-0.04, gene, cex=0.9*fontSize, adj=c(0.5,1))\n        }\n    if (fusions[fusion,&quot;site2&quot;] == &quot;intergenic&quot;)\n        for (gene in unique(exons2$geneName)) {\n            exonsOfGene &lt;- exons2[exons2$geneName == gene &amp; exons2$type != &quot;dummy&quot;,]\n            if (any(exonsOfGene$type == &quot;exon&quot;))\n                text(gene2Offset+mean(c(min(exonsOfGene$left), max(exonsOfGene$right))), yExons-0.04, gene, cex=0.9*fontSize, adj=c(0.5,1))\n        }\n\n    # label breakpoints\n    text(breakpoint1+0.01, yBreakpointLabels-0.03, paste0(&quot;breakpoint1\\n&quot;, fusions[fusion,&quot;display_contig1&quot;], &quot;:&quot;, fusions[fusion,&quot;breakpoint1&quot;]), adj=c(1,0), cex=fontSize)\n    text(gene2Offset+breakpoint2-0.01, yBreakpointLabels-0.03, paste0(&quot;breakpoint2\\n&quot;, fusions[fusion,&quot;display_contig2&quot;], &quot;:&quot;, fusions[fusion,&quot;breakpoint2&quot;]), adj=c(0,0), cex=fontSize)\n\n    # draw coverage axis\n    if (alignmentsFile != &quot;&quot;) {\n        # left axis (gene1)\n        lines(c(-0.02, -0.01, -0.01, -0.02), c(yCoverage, yCoverage, yCoverage+0.1, yCoverage+0.1))\n        text(-0.025, yCoverage, &quot;0&quot;, adj=c(1,0.5), cex=0.9*fontSize)\n        text(-0.025, yCoverage+0.1, coverageNormalization1, adj=c(1,0.5), cex=0.9*fontSize)\n        text(-0.05, yCoverage+0.08, &quot;Coverage&quot;, srt=90, cex=0.9*fontSize, adj=c(1,0.5))\n\n        # right axis (gene2)\n        if (length(coverageRange) == 2) { # separate axes for gene1 and gene2\n            rightCoverageAxisX &lt;- gene2Offset+max(exons2$right)\n            lines(c(rightCoverageAxisX+0.02, rightCoverageAxisX+0.01, rightCoverageAxisX+0.01, rightCoverageAxisX+0.02), c(yCoverage, yCoverage, yCoverage+0.1, yCoverage+0.1))\n            text(rightCoverageAxisX+0.025, yCoverage, &quot;0&quot;, adj=c(0,0.5), cex=0.9*fontSize)\n            text(rightCoverageAxisX+0.025, yCoverage+0.1, coverageNormalization2, adj=c(0,0.5), cex=0.9*fontSize)\n            text(rightCoverageAxisX+0.05, yCoverage+0.08, &quot;Coverage&quot;, srt=90, cex=0.9*fontSize, adj=c(1,0.5))\n        }\n\n        # plot coverage 1\n        rect(min(exons1$left), yCoverage, max(exons1$right), yCoverage+0.1, col=&quot;#eeeeee&quot;, border=NA)\n        if (squishIntrons) {\n            for (exon in 1:nrow(exons1))\n                if (exons1[exon,&quot;type&quot;] != &quot;CDS&quot;) # don&#039;t draw coverage twice for coding regions\n                    drawCoverage(exons1[exon,&quot;left&quot;], exons1[exon,&quot;right&quot;], yCoverage, coverage1, exons1[exon,&quot;start&quot;], exons1[exon,&quot;end&quot;], color1)\n        } else {\n            drawCoverage(min(exons1$left), max(exons1$right), yCoverage, coverage1, min(exons1$start), max(exons1$end), color1)\n        }\n\n        # plot coverage 2\n        rect(gene2Offset+min(exons2$left), yCoverage, gene2Offset+max(exons2$right), yCoverage+0.1, col=&quot;#eeeeee&quot;, border=NA)\n        if (squishIntrons) {\n            for (exon in 1:nrow(exons2))\n                if (exons2[exon,&quot;type&quot;] != &quot;CDS&quot;) # don&#039;t draw coverage twice for coding regions\n                    drawCoverage(gene2Offset+exons2[exon,&quot;left&quot;], gene2Offset+exons2[exon,&quot;right&quot;], yCoverage, coverage2, exons2[exon,&quot;start&quot;], exons2[exon,&quot;end&quot;], color2)\n        } else {\n            drawCoverage(gene2Offset+min(exons2$left), gene2Offset+max(exons2$right), yCoverage, coverage2, min(exons2$start), max(exons2$end), color2)\n        }\n    }\n\n    # plot gene 1\n    lines(c(min(exons1$left), max(exons1$right)), c(yExons, yExons), col=darkColor1)\n    for (gene in unique(exons1$geneName))\n        drawStrand(min(exons1[exons1$geneName == gene,&quot;left&quot;]), max(exons1[exons1$geneName == gene,&quot;right&quot;]), yExons, darkColor1, head(exons1[exons1$geneName == gene,&quot;strand&quot;],1))\n    for (exon in 1:nrow(exons1))\n        drawExon(exons1[exon,&quot;left&quot;], exons1[exon,&quot;right&quot;], yExons, color1, exons1[exon,&quot;exonNumber&quot;], exons1[exon,&quot;type&quot;])\n\n    # plot gene 2\n    lines(c(gene2Offset, gene2Offset+max(exons2$right)), c(yExons, yExons), col=darkColor2)\n    for (gene in unique(exons2$geneName))\n        drawStrand(gene2Offset+min(exons2[exons2$geneName == gene,&quot;left&quot;]), gene2Offset+max(exons2[exons2$geneName == gene,&quot;right&quot;]), yExons, darkColor2, head(exons2[exons2$geneName == gene,&quot;strand&quot;],1))\n    for (exon in 1:nrow(exons2))\n        drawExon(gene2Offset+exons2[exon,&quot;left&quot;], gene2Offset+exons2[exon,&quot;right&quot;], yExons, color2, exons2[exon,&quot;exonNumber&quot;], exons2[exon,&quot;type&quot;])\n\n    # plot gene1 of fusion\n    if (fusions[fusion,&quot;direction1&quot;] == &quot;downstream&quot;) {\n        # plot strands\n        lines(c(fusionOffset1, fusionOffset1+breakpoint1), c(yFusion, yFusion), col=darkColor1)\n        for (gene in unique(exons1$geneName)) {\n            exonsOfGene &lt;- exons1[exons1$geneName == gene,]\n            if (min(exonsOfGene$start) &lt;= fusions[fusion,&quot;breakpoint1&quot;])\n                drawStrand(fusionOffset1+min(exonsOfGene$left), fusionOffset1+min(breakpoint1, max(exonsOfGene$right)), yFusion, col=darkColor1, exonsOfGene$strand[1])\n        }\n        # plot exons\n        for (exon in 1:nrow(exons1))\n            if (exons1[exon,&quot;start&quot;] &lt;= fusions[fusion,&quot;breakpoint1&quot;])\n                drawExon(fusionOffset1+exons1[exon,&quot;left&quot;], fusionOffset1+min(breakpoint1, exons1[exon,&quot;right&quot;]), yFusion, color1, exons1[exon,&quot;exonNumber&quot;], exons1[exon,&quot;type&quot;])\n        # plot trajectories\n        lines(c(0, 0, fusionOffset1), c(yTrajectoryExonTop, yTrajectoryExonBottom, yTrajectoryFusion), col=&quot;red&quot;, lty=2)\n        lines(c(breakpoint1, breakpoint1, fusionOffset1+breakpoint1), c(yTrajectoryBreakpointLabels, yTrajectoryExonBottom, yTrajectoryFusion), col=&quot;red&quot;, lty=2)\n    } else if (fusions[fusion,&quot;direction1&quot;] == &quot;upstream&quot;) {\n        # plot strands\n        lines(c(fusionOffset1, fusionOffset2), c(yFusion, yFusion), col=darkColor1)\n        for (gene in unique(exons1$geneName)) {\n            exonsOfGene &lt;- exons1[exons1$geneName == gene,]\n            if (max(exonsOfGene$end+1) &gt;= fusions[fusion,&quot;breakpoint1&quot;])\n                drawStrand(fusionOffset2-max(exonsOfGene$right)+breakpoint1, min(fusionOffset2, fusionOffset2-min(exonsOfGene$left)+breakpoint1), yFusion, col=darkColor1, chartr(&quot;+-&quot;, &quot;-+&quot;, exonsOfGene$strand[1]))\n        }\n        # plot exons\n        for (exon in 1:nrow(exons1))\n            if (exons1[exon,&quot;end&quot;]+1 &gt;= fusions[fusion,&quot;breakpoint1&quot;])\n                drawExon(fusionOffset1+max(exons1$right)-exons1[exon,&quot;right&quot;], min(fusionOffset2, fusionOffset1+max(exons1$right)-exons1[exon,&quot;left&quot;]), yFusion, color1, exons1[exon,&quot;exonNumber&quot;], exons1[exon,&quot;type&quot;])\n        # plot trajectories\n        lines(c(max(exons1$right), max(exons1$right), fusionOffset1), c(yTrajectoryExonTop, yTrajectoryExonBottom, yTrajectoryFusion), col=&quot;red&quot;, lty=2)\n        lines(c(breakpoint1, breakpoint1, fusionOffset1+max(exons1$right)-breakpoint1), c(yTrajectoryBreakpointLabels, yTrajectoryExonBottom, yTrajectoryFusion), col=&quot;red&quot;, lty=2)\n    }\n\n    # plot gene2 of fusion\n    if (fusions[fusion,&quot;direction2&quot;] == &quot;downstream&quot;) {\n        # plot strands\n        lines(c(fusionOffset2, fusionOffset2+breakpoint2), c(yFusion, yFusion), col=darkColor2)\n        for (gene in unique(exons2$geneName)) {\n            exonsOfGene &lt;- exons2[exons2$geneName == gene,]\n            if (min(exonsOfGene$start) &lt;= fusions[fusion,&quot;breakpoint2&quot;])\n                drawStrand(max(fusionOffset2, fusionOffset2+breakpoint2-max(exonsOfGene$right)), fusionOffset2+breakpoint2-min(exonsOfGene$left), yFusion, col=darkColor2, chartr(&quot;+-&quot;, &quot;-+&quot;, exonsOfGene$strand[1]))\n        }\n        # plot exons\n        for (exon in 1:nrow(exons2))\n            if (exons2[exon,&quot;start&quot;] &lt;= fusions[fusion,&quot;breakpoint2&quot;])\n                drawExon(max(fusionOffset2, fusionOffset2+breakpoint2-exons2[exon,&quot;right&quot;]), fusionOffset2+breakpoint2-exons2[exon,&quot;left&quot;], yFusion, color2, exons2[exon,&quot;exonNumber&quot;], exons2[exon,&quot;type&quot;])\n        # plot trajectories\n        lines(c(gene2Offset, gene2Offset, fusionOffset2+breakpoint2), c(yTrajectoryExonTop, yTrajectoryExonBottom, yTrajectoryFusion), col=&quot;red&quot;, lty=2)\n        lines(c(gene2Offset+breakpoint2, gene2Offset+breakpoint2, fusionOffset2), c(yTrajectoryBreakpointLabels, yTrajectoryExonBottom, yTrajectoryFusion), col=&quot;red&quot;, lty=2)\n    } else if (fusions[fusion,&quot;direction2&quot;] == &quot;upstream&quot;) {\n        # plot strands\n        lines(c(fusionOffset2, fusionOffset2+max(exons2$right)-breakpoint2), c(yFusion, yFusion), col=darkColor2)\n        for (gene in unique(exons2$geneName)) {\n            exonsOfGene &lt;- exons2[exons2$geneName == gene,]\n            if (max(exonsOfGene$end+1) &gt;= fusions[fusion,&quot;breakpoint2&quot;])\n                drawStrand(max(fusionOffset2, fusionOffset2+min(exonsOfGene$left)-breakpoint2), fusionOffset2+max(exonsOfGene$right)-breakpoint2, yFusion, col=darkColor2, exonsOfGene$strand[1])\n        }\n        # plot exons\n        for (exon in 1:nrow(exons2))\n            if (exons2[exon,&quot;end&quot;]+1 &gt;= fusions[fusion,&quot;breakpoint2&quot;])\n                drawExon(max(fusionOffset2, fusionOffset2+exons2[exon,&quot;left&quot;]-breakpoint2), fusionOffset2+exons2[exon,&quot;right&quot;]-breakpoint2, yFusion, color2, exons2[exon,&quot;exonNumber&quot;], exons2[exon,&quot;type&quot;])\n        # plot trajectories\n        lines(c(gene2Offset+max(exons2$right), gene2Offset+max(exons2$right), fusionOffset2+max(exons2$right)-breakpoint2), c(yTrajectoryExonTop, yTrajectoryExonBottom, yTrajectoryFusion), col=&quot;red&quot;, lty=2)\n        lines(c(gene2Offset+breakpoint2, gene2Offset+breakpoint2, fusionOffset2), c(yTrajectoryBreakpointLabels, yTrajectoryExonBottom, yTrajectoryFusion), col=&quot;red&quot;, lty=2)\n    }\n\n    if (fusions[fusion,&quot;fusion_transcript&quot;] != &quot;.&quot;) {\n        # print fusion transcript colored by gene of origin\n        fusion_transcript1 &lt;- gsub(&quot;\\\\|.*&quot;, &quot;&quot;, fusions[fusion,&quot;fusion_transcript&quot;], perl=T)\n        fusion_transcript1 &lt;- substr(fusion_transcript1, max(1, nchar(fusion_transcript1)-30), nchar(fusion_transcript1))\n        fusion_transcript2 &lt;- gsub(&quot;.*\\\\|&quot;, &quot;&quot;, fusions[fusion,&quot;fusion_transcript&quot;], perl=T)\n        fusion_transcript2 &lt;- substr(fusion_transcript2, 1, min(nchar(fusion_transcript2), 30))\n        # check for non-template bases\n        non_template_bases &lt;- gsub(&quot;.*\\\\|([^|]*)\\\\|.*&quot;, &quot;\\\\1&quot;, fusions[fusion,&quot;fusion_transcript&quot;], perl=T)\n        if (non_template_bases == fusions[fusion,&quot;fusion_transcript&quot;]) # no non-template bases found\n            non_template_bases &lt;- &quot;&quot;\n        # divide non-template bases half-and-half for centered alignment\n        non_template_bases1 &lt;- substr(non_template_bases, 1, floor(nchar(non_template_bases)\/2))\n        non_template_bases2 &lt;- substr(non_template_bases, ceiling(nchar(non_template_bases)\/2+0.5), nchar(non_template_bases))\n        # transcript 1\n        text(fusionOffset2, yTranscript, bquote(.(fusion_transcript1) * phantom(.(non_template_bases1))), col=darkColor1, adj=c(1,0.5), cex=fontSize)\n        # transcript 2\n        text(fusionOffset2, yTranscript, bquote(phantom(.(non_template_bases2)) * .(fusion_transcript2)), col=darkColor2, adj=c(0,0.5), cex=fontSize)\n        # non-template bases\n        text(fusionOffset2, yTranscript, non_template_bases1, adj=c(1,0.5), cex=fontSize)\n        text(fusionOffset2, yTranscript, non_template_bases2, adj=c(0,0.5), cex=fontSize)\n    }\n\n    # draw scale\n    realScale &lt;- max(exons1$end - exons1$start, exons2$end - exons2$start)\n    mapScale &lt;- max(exons1$right - exons1$left, exons2$right - exons2$left)\n    # choose scale which is closest to desired scale length\n    desiredScaleSize &lt;- 0.2\n    realScale &lt;- desiredScaleSize \/ mapScale * realScale\n    mapScale &lt;- desiredScaleSize\n    realScaleOptimalFit &lt;- signif(realScale, 1) # round to most significant digit\n    mapScaleOptimalFit &lt;- realScaleOptimalFit \/ realScale * mapScale\n    # draw scale line\n    lines(c(1-mapScaleOptimalFit, 1), c(yScale, yScale)) # scale line\n    lines(c(1-mapScaleOptimalFit, 1-mapScaleOptimalFit), c(yScale-0.007, yScale+0.007)) # left whisker\n    lines(c(1, 1), c(yScale-0.007, yScale+0.007)) # right whisker\n    # draw units above scale line\n    realScaleThousands &lt;- max(0, min(3, floor(log10(realScaleOptimalFit)\/3)))\n    scaleUnits &lt;- c(&quot;bp&quot;, &quot;kbp&quot;, &quot;Mbp&quot;, &quot;Gbp&quot;)\n    scaleLabel &lt;- paste(realScaleOptimalFit\/max(1,1000^realScaleThousands), scaleUnits[realScaleThousands+1])\n    text(1-mapScaleOptimalFit\/2, yScale+0.005, scaleLabel, adj=c(0.5,0), cex=fontSize*0.9)\n    if (squishIntrons)\n        text(1-mapScaleOptimalFit\/2, yScale-0.005, &quot;introns not to scale&quot;, adj=c(0.5,1), cex=fontSize*0.9, font=3)\n\n    # draw circos plot\n    if (is.null(cytobands) || !(&quot;circlize&quot; %in% names(sessionInfo()$otherPkgs)) || !(&quot;GenomicRanges&quot; %in% names(sessionInfo()$otherPkgs))) {\n        plot(0, 0, type=&quot;l&quot;, xlim=c(0, 1), ylim=c(0, 1), bty=&quot;n&quot;, xaxt=&quot;n&quot;, yaxt=&quot;n&quot;, xlab=&quot;&quot;, ylab=&quot;&quot;)\n        plot(0, 0, type=&quot;l&quot;, xlim=c(0, 1), ylim=c(0, 1), bty=&quot;n&quot;, xaxt=&quot;n&quot;, yaxt=&quot;n&quot;, xlab=&quot;&quot;, ylab=&quot;&quot;)\n    } else {\n        par(mar=c(2,4,0,0), xpd=NA)\n        drawCircos(fusion, fusions, cytobands, minConfidenceForCircosPlot, circosColors)\n        par(mar=c(0,0,0,0), xpd=F)\n    }\n\n    # draw protein domains\n    plot(0, 0, type=&quot;l&quot;, xlim=c(-0.1, 1.1), ylim=c(0, 1), bty=&quot;n&quot;, xaxt=&quot;n&quot;, yaxt=&quot;n&quot;, xlab=&quot;&quot;, ylab=&quot;&quot;)\n    par(xpd=NA)\n    if (!is.null(proteinDomains) &amp;&amp; &quot;GenomicRanges&quot; %in% names(sessionInfo()$otherPkgs))\n        drawProteinDomains(fusions[fusion,], exons1, exons2, proteinDomains, color1, color2, mergeDomainsOverlappingBy, optimizeDomainColors)\n    par(xpd=F)\n\n    # print statistics about supporting alignments\n    plot(0, 0, type=&quot;l&quot;, xlim=c(0, 1), ylim=c(0, 1), bty=&quot;n&quot;, xaxt=&quot;n&quot;, yaxt=&quot;n&quot;, xlab=&quot;&quot;, ylab=&quot;&quot;)\n    text(0, 0.575, &quot;SUPPORTING READ COUNT&quot;, font=2, adj=c(0,0), cex=fontSize)\n    if (&quot;split_reads&quot; %in% colnames(fusions)) { # STAR-Fusion reports split reads from both breakpoints combined\n        text(0, 0.525, paste0(&quot;Split reads = &quot;, fusions[fusion,&quot;split_reads&quot;], &quot;\\n&quot;, &quot;Discordant mates = &quot;, fusions[fusion,&quot;discordant_mates&quot;]), adj=c(0,1), cex=fontSize)\n    } else { # Arriba reports split reads separately for the two breakpoints\n        text(\n            0, 0.525,\n            paste0(\n                &quot;Split reads at breakpoint1 = &quot;, fusions[fusion,&quot;split_reads1&quot;], &quot;\\n&quot;,\n                &quot;Split reads at breakpoint2 = &quot;, fusions[fusion,&quot;split_reads2&quot;], &quot;\\n&quot;,\n                &quot;Discordant mates = &quot;, fusions[fusion,&quot;discordant_mates&quot;]\n            ),\n            adj=c(0,1), cex=fontSize\n        )\n    }\n\n}\n\ndevNull &lt;- dev.off()\nmessage(&quot;Done&quot;)\n<\/code><\/pre>\n<h3>\u6784\u5efabash\u811a\u672c\u7528\u4e8e\u6279\u91cf\u8fd0\u884cArriba<\/h3>\n<pre><code class=\"language-bash\">cd arriba_fusion_vis\n\n# \u6ce8\u610fcytobands\u7248\u672c\u8981\u4e0estar-fusion\u7248\u672c\u4e00\u81f4\uff0cannotation\u8981\u4e0estar-fusion\u7684annotation\u7248\u672c\u4e00\u81f4\n# \u6bd4\u5982\u6b64\u5904starfusion\u7528\u7684\u662fGRCh38_gencode_v37_CTAT_lib_Mar012021.plug-n-play\n# \u90a3\u4e48\uff0c\u8fd9\u91cc\u7684annotation\u7528\u7684\u5c31\u8981\u662fgencode.v37.annotation.gtf\n\nfor n in `ls ..\/fusion_hg38\/*star-fusion.fusion_predictions.tsv`;do echo Rscript .\/draw_fusions.R --fusions=$n --output=$(basename &quot;$n&quot; star-fusion.fusion_predictions.tsv)_fusion.pdf --cytobands=\/home\/zhoukaiwen\/software\/anaconda3\/envs\/arriba\/var\/lib\/arriba\/cytobands_hg38_GRCh38_v2.4.0.tsv --annotation=\/home\/zhoukaiwen\/database\/GENCODE\/gencode.v37.annotation.gtf --proteinDomains=\/home\/zhoukaiwen\/software\/anaconda3\/envs\/arriba\/var\/lib\/arriba\/protein_domains_hg38_GRCh38_v2.4.0.gff3&gt;&gt;.\/arriba_vis.sh;done\n\nnohup .\/arriba_vis.sh&gt;arriba_vis.log 2&gt;&amp;1 &amp;<\/code><\/pre>\n","protected":false},"excerpt":{"rendered":"<p>Arriba\u63d0\u4f9b\u7684draw_fusions.R https:\/\/github.com\/suhrig\/arrib&#8230;<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-135","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"_links":{"self":[{"href":"https:\/\/www.kz-hub.tech\/index.php\/wp-json\/wp\/v2\/posts\/135","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.kz-hub.tech\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.kz-hub.tech\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.kz-hub.tech\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.kz-hub.tech\/index.php\/wp-json\/wp\/v2\/comments?post=135"}],"version-history":[{"count":2,"href":"https:\/\/www.kz-hub.tech\/index.php\/wp-json\/wp\/v2\/posts\/135\/revisions"}],"predecessor-version":[{"id":137,"href":"https:\/\/www.kz-hub.tech\/index.php\/wp-json\/wp\/v2\/posts\/135\/revisions\/137"}],"wp:attachment":[{"href":"https:\/\/www.kz-hub.tech\/index.php\/wp-json\/wp\/v2\/media?parent=135"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.kz-hub.tech\/index.php\/wp-json\/wp\/v2\/categories?post=135"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.kz-hub.tech\/index.php\/wp-json\/wp\/v2\/tags?post=135"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}