Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ <h3> 📋 Reactome Knowledgebase Details </h3>
<p>Loading details of {{ state.select() || state.pathwayId() }}</p>
</div>
} @else if (obj()) {
<cr-description-tab [obj]="obj()!" [analysisResult]="analysis.result()"></cr-description-tab>
<cr-description-tab [obj]="obj()!" [analysisResult]="analysis.result()" [showReactionDiagram]="false"></cr-description-tab>
}
</ng-template>
</mat-tab>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ export class DescriptionTabComponent implements OnDestroy {
readonly obj = input.required<SelectableObject>();
readonly analysisResult = input<Analysis.Result>();
readonly showLocations = input(false);
readonly showReactionDiagram = input(true);

static referenceTypeToNameSuffix = new Map<string, string>([
["ReferenceMolecule", ""],
Expand Down Expand Up @@ -325,7 +326,7 @@ export class DescriptionTabComponent implements OnDestroy {
label: 'Reaction Diagram',
manual: true,
template: this.reactionDiagramTemplate$ as Signal<TemplateRef<any>>,
isPresent: this.isReaction
isPresent: computed(() => this.isReaction() && this.showReactionDiagram())
},
{key: DataKeys.REFERENCE_ENTITY, label: Labels.EXTERNAL_REFERENCE, manual: true, template: this.referenceTemplate$},
{key: DataKeys.SUMMARISED_ENTITIES, label: Labels.SUMMARISED_ENTITIES},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -678,7 +678,7 @@ export class DiagramComponent implements AfterViewInit, OnDestroy {
cy.batch(() => {
this.setSubPathwayVisibility(true, cy);
cy.elements().removeClass('flag');
cy.edges('![?color]').style({'underlay-opacity': 0})
cy.edges().not('[?color]').style({'underlay-opacity': 0})
})

return cy.collection()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<div id="hierarchy-container">
<div id="breadcrumb-container">
<a routerLink="" preserveFragment="true" queryParamsHandling="merge" matTooltip="Pathways overview" [queryParams]="{select: state.pathwayId()}">
<a routerLink="/PathwayBrowser" preserveFragment="true" queryParamsHandling="merge" matTooltip="Pathways overview" [queryParams]="{select: state.pathwayId()}">
<ng-container class="icon-container">
<mat-icon class='home-icon custom-icon primary' svgIcon="home"></mat-icon>
</ng-container>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,21 @@
import {Inject, LOCALE_ID, Pipe, PipeTransform} from '@angular/core';
import {DatePipe} from "@angular/common";
import {formatDate} from "@angular/common";

@Pipe({
name: 'authorshipDateFormat',
standalone: true
})
export class AuthorshipDateFormatPipe implements PipeTransform {
constructor(private datePipe: DatePipe, @Inject(LOCALE_ID) private locale: string) {
constructor(@Inject(LOCALE_ID) private locale: string) {
}

transform(dateTime: string) {
if (!dateTime) return;
return this.datePipe.transform(dateTime);
try {
return formatDate(dateTime, 'mediumDate', this.locale);
} catch {
return;
}
}

}
88 changes: 70 additions & 18 deletions projects/pathway-browser/src/app/services/diagram.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@ const squaredDist = (pos1: Position, pos2: Position) => {

const dist = (pos1: Position, pos2: Position) => Math.sqrt(squaredDist(pos1, pos2))

const isFinitePoint = (point: Position | undefined | null): point is Position =>
!!point && Number.isFinite(point.x) && Number.isFinite(point.y);

const closestToAverage = (positions: Position[]): Position => {
const average = avg(positions);
let closest = positions[0];
Expand Down Expand Up @@ -562,8 +565,13 @@ export class DiagramService {
this.addEdgeInfo(reaction, points, 'forward', targetP);

let [from, to] = [points.shift()!, points.pop()!]
from = from ?? nodeP; // Quick fix to avoid problem with reaction without visible outputs like R-HSA-2424252 in R-HSA-1474244
to = to ?? reactionP; // Quick fix to avoid problem with reaction without visible outputs like R-HSA-2424252 in R-HSA-1474244
// Keep fallback direction aligned with edge source/target for connectors that have missing segment endpoints.
from = from ?? sourceP;
to = to ?? targetP;
if (equal(from, to)) {
from = sourceP;
to = targetP;
}
if (connector.type === 'CATALYST' && connector.endShape) {
to = scale(connector.endShape.centre || connector.endShape.c);
}
Expand All @@ -582,7 +590,7 @@ export class DiagramService {
if (equal(from, reactionP) || equal(to, reactionP)) d -= REACTION_RADIUS;
if (classes.includes('positive-regulation') || classes.includes('catalysis') || classes.includes('production')) d -= ARROW_MULT * T;
// console.assert(d > MIN_DIST, `The edge between reaction: R-HSA-${reaction.reactomeId} and entity: R-HSA-${node.reactomeId} in pathway ${id} has a visible length of ${d} which is shorter than ${MIN_DIST}`)
console.assert(d > MIN_DIST, `${id}\t${diagram.displayName}\t${hasFadeOut}\tR-HSA-${reaction.reactomeId}\tR-HSA-${node.reactomeId}\thttps://release.reactome.org/PathwayBrowser/#/${id}&SEL=R-HSA-${reaction.reactomeId}&FLG=R-HSA-${node.reactomeId}\thttps://reactome-pwp.github.io/PathwayBrowser/${id}?select=${reaction.reactomeId}&flag=${node.reactomeId}`)
console.assert(Math.abs(d) >= MIN_DIST, `${id}\t${diagram.displayName}\t${hasFadeOut}\tR-HSA-${reaction.reactomeId}\tR-HSA-${node.reactomeId}\thttps://release.reactome.org/PathwayBrowser/#/${id}&SEL=R-HSA-${reaction.reactomeId}&FLG=R-HSA-${node.reactomeId}\thttps://reactome-pwp.github.io/PathwayBrowser/${id}?select=${reaction.reactomeId}&flag=${node.reactomeId}`)

let replacement, replacedBy;
if (connector.isFadeOut) {
Expand All @@ -595,25 +603,28 @@ export class DiagramService {
}
if (!connector.isFadeOut) {
// First case: same node is used both special and normal context
replacement = node.connectors.find(otherConnector => otherConnector !== connector && otherConnector.isFadeOut && samePoint(idToEdges.get(otherConnector.edgeId)!.position, reaction.position))?.edgeId;
replacement = node.connectors.find(otherConnector => {
if (otherConnector === connector || !otherConnector.isFadeOut) return false;
const otherEdge = idToEdges.get(otherConnector.edgeId);
return samePoint(otherEdge?.position, reaction.position);
})?.edgeId;
// console.log("Reaction edge", replacement)

// Second case: different nodes are used between special and normal context
replacement = replacement || (posToNormalNode.get(pointToStr(node.position)) && posToNormalEdge.get(pointToStr(reaction.position)))?.id;
// console.log("Reaction edge", replacement)

}
const relativeSegments = this.getRelativeSegmentsData(relatives);
const edge: cytoscape.EdgeDefinition = {
data: {
id: this.getEdgeId(source, connector, target, edgeIds),
graph: dbIdToGraphEdge.get(reaction.reactomeId),
source: source.id + '',
target: target.id + '',
stoichiometry: connector.stoichiometry.value,
weights: relatives.weights.join(" "),
distances: relatives.distances.join(" "),
sourceEndpoint: this.endpoint(sourceP, from),
targetEndpoint: this.endpoint(targetP, to),
...relativeSegments,
...this.getEndpointData(sourceP, targetP, from, to),
pathway: eventIdToSubPathwayId.get(reaction.reactomeId),
reactomeId: reaction.reactomeId,
reactionId: reaction.id,
Expand Down Expand Up @@ -645,9 +656,14 @@ export class DiagramService {
let [from, to] = [points.shift()!, points.pop()!]
from = from ?? sourceP; // Quick fix to avoid problem with reaction without visible outputs like R-HSA-2424252 in R-HSA-1474244
to = to ?? targetP; // Quick fix to avoid problem with reaction without visible outputs like R-HSA-2424252 in R-HSA-1474244
if (equal(from, to)) {
from = sourceP;
to = targetP;
}

// points = addRoundness(from, to, points);
const relatives = this.absoluteToRelative(from, to, points);
const relativeSegments = this.getRelativeSegmentsData(relatives);

const classes = [...this.linkClassMap.get(link.renderableClass)!];
if (link.isDisease) classes.push('disease');
Expand All @@ -660,10 +676,8 @@ export class DiagramService {
id: link.id + '',
source: link.inputs[0].id + '',
target: link.outputs[0].id + '',
weights: relatives.weights.join(" "),
distances: relatives.distances.join(" "),
sourceEndpoint: this.endpoint(sourceP, from),
targetEndpoint: this.endpoint(targetP, to),
...relativeSegments,
...this.getEndpointData(sourceP, targetP, from, to),
isFadeOut: link.isFadeOut,
isBackground: isBackground
},
Expand Down Expand Up @@ -742,8 +756,38 @@ export class DiagramService {
}
}

private endpoint(source: Position, point: Position): string {
return `${point.x - source.x} ${point.y - source.y}`
private endpoint(source: Position, point: Position): string | undefined {
const dx = point.x - source.x;
const dy = point.y - source.y;
if (!Number.isFinite(dx) || !Number.isFinite(dy)) return undefined;
return `${dx} ${dy}`
}

private getEndpointData(source: Position, target: Position, from: Position, to: Position): Partial<{ sourceEndpoint: string, targetEndpoint: string }> {
if (!isFinitePoint(source) || !isFinitePoint(target) || !isFinitePoint(from) || !isFinitePoint(to)) {
return {};
}
if (equal(source, target)) {
return {};
}
const sourceEndpoint = this.endpoint(source, from);
const targetEndpoint = this.endpoint(target, to);
if (!sourceEndpoint || !targetEndpoint) {
return {};
}
return {sourceEndpoint, targetEndpoint};
}

private getRelativeSegmentsData(relatives: RelativePosition): Partial<{weights: string, distances: string}> {
const weights = relatives.weights.filter(value => Number.isFinite(value));
const distances = relatives.distances.filter(value => Number.isFinite(value));
if (weights.length === 0 || distances.length === 0 || weights.length !== distances.length) {
return {};
}
return {
weights: weights.join(" "),
distances: distances.join(" "),
};
}


Expand All @@ -761,19 +805,26 @@ export class DiagramService {
const relatives: RelativePosition = {distances: [], weights: []};
if (toConvert.length === 0) return relatives;

if (!isFinitePoint(source) || !isFinitePoint(target) || equal(source, target)) return relatives;

const mainVector = array([target.x - source.x, target.y - source.y]); // Edge vector
if (mainVector.x === 0 && mainVector.y === 0) return relatives;
const orthoVector = array([-mainVector.y, mainVector.x]) // Perpendicular vector
.normalize(); //Normalized to have the distance expressed in pixels https://math.stackexchange.com/a/413235/683621
let transform = array([
const transform = array([
[mainVector.x, mainVector.y],
[orthoVector.x, orthoVector.y],
]).inv(); // Should always be invertible if the ortho vector is indeed perpendicular

for (let coord of toConvert) {
if (!isFinitePoint(coord)) continue;
const absolute = array([[coord.x - source.x, coord.y - source.y]]);
const relative = absolute.multiply(transform);
relatives.weights.push(relative.get(0, 0))
relatives.distances.push(relative.get(0, 1))
const weight = relative.get(0, 0);
const distance = relative.get(0, 1);
if (!Number.isFinite(weight) || !Number.isFinite(distance)) continue;
relatives.weights.push(weight)
relatives.distances.push(distance)
}
return relatives;
}
Expand Down Expand Up @@ -869,7 +920,8 @@ export class DiagramService {
}
}

function samePoint(p1: Position, p2: Position) {
function samePoint(p1?: Position | null, p2?: Position | null) {
if (!p1 || !p2) return false;
return p1.x === p2.x && p1.y === p2.y
}

Expand Down
Loading