Rewrite the graphing code

This uses more Cocoa classes to draw the lines, making it easier to
understand and hopefully maintain.

Furthermore, we use less memory now, which is nice, but all the
dynamic arrays probably mean more CPU usage.
This commit is contained in:
Pieter de Bie
2008-08-27 21:51:42 +02:00
parent b99e1a85d6
commit bbeedd10ce
9 changed files with 177 additions and 87 deletions
+12
View File
@@ -29,6 +29,8 @@
F56526240E03D85900F03B52 /* WebKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F56526230E03D85900F03B52 /* WebKit.framework */; };
F565262B0E03D89B00F03B52 /* PBWebGitController.m in Sources */ = {isa = PBXBuildFile; fileRef = F565262A0E03D89B00F03B52 /* PBWebGitController.m */; };
F565265A0E03E71B00F03B52 /* commit.html in Resources */ = {isa = PBXBuildFile; fileRef = F56526590E03E71B00F03B52 /* commit.html */; };
F56CC7290E65E0AD004307B4 /* PBLine.m in Sources */ = {isa = PBXBuildFile; fileRef = F56CC7280E65E0AD004307B4 /* PBLine.m */; };
F56CC7320E65E0E5004307B4 /* PBGraphCellInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = F56CC7310E65E0E5004307B4 /* PBGraphCellInfo.m */; };
F57ABE0B0E0442DD00A088B8 /* commit.js in Resources */ = {isa = PBXBuildFile; fileRef = F57ABDDE0E0441DE00A088B8 /* commit.js */; };
F57ABE2B0E04435100A088B8 /* prototype.js in Resources */ = {isa = PBXBuildFile; fileRef = F57ABE180E04431D00A088B8 /* prototype.js */; };
F57CC3910E05DDF2000472E2 /* PBEasyPipe.m in Sources */ = {isa = PBXBuildFile; fileRef = F57CC3900E05DDF2000472E2 /* PBEasyPipe.m */; };
@@ -90,6 +92,10 @@
F56526290E03D89B00F03B52 /* PBWebGitController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBWebGitController.h; sourceTree = "<group>"; };
F565262A0E03D89B00F03B52 /* PBWebGitController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBWebGitController.m; sourceTree = "<group>"; };
F56526590E03E71B00F03B52 /* commit.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; name = commit.html; path = html/commit.html; sourceTree = "<group>"; };
F56CC7270E65E0AD004307B4 /* PBLine.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBLine.h; sourceTree = "<group>"; };
F56CC7280E65E0AD004307B4 /* PBLine.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBLine.m; sourceTree = "<group>"; };
F56CC7300E65E0E5004307B4 /* PBGraphCellInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBGraphCellInfo.h; sourceTree = "<group>"; };
F56CC7310E65E0E5004307B4 /* PBGraphCellInfo.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBGraphCellInfo.m; sourceTree = "<group>"; };
F57ABDDE0E0441DE00A088B8 /* commit.js */ = {isa = PBXFileReference; explicitFileType = sourcecode.javascript; fileEncoding = 4; name = commit.js; path = html/commit.js; sourceTree = "<group>"; };
F57ABE180E04431D00A088B8 /* prototype.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; name = prototype.js; path = html/prototype.js; sourceTree = "<group>"; };
F57CC38F0E05DDF2000472E2 /* PBEasyPipe.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBEasyPipe.h; sourceTree = "<group>"; };
@@ -232,6 +238,8 @@
F56174540E05887E001DCD79 /* Git */ = {
isa = PBXGroup;
children = (
F56CC7300E65E0E5004307B4 /* PBGraphCellInfo.h */,
F56CC7310E65E0E5004307B4 /* PBGraphCellInfo.m */,
F5FF4E780E082E440006317A /* PBGitGrapher.h */,
F5FF4E790E082E440006317A /* PBGitGrapher.m */,
F5945E150E02B0C200706420 /* PBGitRepository.h */,
@@ -277,6 +285,8 @@
F513085A0E0740F2000C8BCD /* PBQLOutlineView.m */,
F50FE0E10E07BE9600854FCD /* PBGitRevisionCell.h */,
F50FE0E20E07BE9600854FCD /* PBGitRevisionCell.m */,
F56CC7270E65E0AD004307B4 /* PBLine.h */,
F56CC7280E65E0AD004307B4 /* PBLine.m */,
);
name = Aux;
sourceTree = "<group>";
@@ -393,6 +403,8 @@
F5FF4E7A0E082E440006317A /* PBGitGrapher.m in Sources */,
911111F80E594F3F00BF76B4 /* PBRepositoryDocumentController.m in Sources */,
913D5E5F0E556A9300CECEA2 /* PBCLIProxy.mm in Sources */,
F56CC7290E65E0AD004307B4 /* PBLine.m in Sources */,
F56CC7320E65E0E5004307B4 /* PBGraphCellInfo.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
+4 -18
View File
@@ -8,7 +8,8 @@
#import <Cocoa/Cocoa.h>
#import "PBGitCommit.h"
#import "PBLine.h"
#import "PBGraphCellInfo.h"
struct PBGitGraphColumn {
NSString* commit; // Commit that we're looking for
@@ -18,25 +19,10 @@ struct PBGitGraphColumn {
#define PBGitMaxColumns 100
struct PBGitGraphCellInfo {
struct PBGitGraphColumn columns[PBGitMaxColumns];
int upperMapping[PBGitMaxColumns]; //How are the offsets compared to previous cell?
int lowerMapping[PBGitMaxColumns]; //How are the offsets compared to this cell?
int position;
NSString* commit; // Commit in cell
int numColumns;
int numNewColumns;
};
void add_commit_to_graph(struct PBGitGraphCellInfo* info, NSString* parent, int* mapping_index);
typedef struct PBGitGraphCellInfo PBGitCellInfo;
@interface PBGitGrapher : NSObject {
PBGitCellInfo* cellsInfo;
NSMutableArray* cellsInfo;
}
- (void) parseCommits: (NSArray *) array;
- (struct PBGitGraphCellInfo) cellInfoForRow: (int) row;
- (PBGraphCellInfo*) cellInfoForRow: (int) row;
@end
+52 -56
View File
@@ -14,73 +14,67 @@
- (void) parseCommits: (NSArray *) commits
{
cellsInfo = malloc(sizeof(struct PBGitGraphCellInfo) * [commits count]);
memset(cellsInfo, 0, sizeof(struct PBGitGraphCellInfo) * [commits count]);
cellsInfo = [NSMutableArray arrayWithCapacity: [commits count]];
int row = 0;
struct PBGitGraphCellInfo *previous = nil;
for (PBGitCommit* commit in commits) {
struct PBGitGraphCellInfo *info = &(cellsInfo[row]);
info->commit = commit.sha;
info->numColumns = 0;
PBGraphCellInfo* previous;
NSMutableArray* previousLanes = [NSMutableArray array];
for (PBGitCommit* commit in commits) {
int i = 0, newPos = -1;
for (i = 0; i < PBGitMaxColumns; i++) {
info->lowerMapping[i] = -1;
info->upperMapping[i] = -1;
}
NSMutableArray* currentLanes = [NSMutableArray array];
NSMutableArray* lines = [NSMutableArray array];
BOOL didFirst = NO;
// First, iterate over earlier columns and pass through any that don't want this commit
if (previous != nil) {
// We can't count until numColumns here, as it's only used for the width of the cell.
for (i = 0; i < PBGitMaxColumns; i++) {
if ((previous->columns[i].commit) == nil)
continue;
for (NSString* lane in previousLanes) {
i++;
// This is our commit! We should do a "merge": move the line from
// our upperMapping to their lowerMapping
if ([previous->columns[i].commit isEqualToString:info->commit]) {
if ([lane isEqualToString:commit.sha]) {
if (!didFirst) {
didFirst = YES;
info->position = info->numColumns++;
info->columns[info->position].commit = [commit.parents objectAtIndex:0];
[currentLanes addObject: [commit.parents objectAtIndex:0]];
newPos = [currentLanes count];
}
newPos = info->position;
info->upperMapping[i] = newPos;
[lines addObject: [PBLine upperLineFrom: i to: newPos]];
}
else {
// We are not this commit.
// Try to find an earlier column for this commit.
int j;
int j = 0;
BOOL found = NO;
for (j = 0; j < info->numColumns; j++) {
if (j == info->position)
for (NSString* column in currentLanes) {
j++;
// ??? what is this?
if (j == newPos)
continue;
if ([previous->columns[i].commit isEqualToString: info->columns[j].commit]) {
if ([column isEqualToString: lane]) {
// We already have a column for this commit. use it instead
newPos = j;
info->upperMapping[previous->lowerMapping[i]] = newPos;
[lines addObject: [PBLine lowerLineFrom: i to: j]];
found = YES;
break;
}
}
// We need a new column for this.
if (!found) {
if (previous->columns[i].color == 10)
continue;
// This was used as a hack to stop large lanes from drawing
//if (previous->columns[i].color == 10)
// continue;
newPos = info->numColumns++;
info->columns[newPos] = previous->columns[i];
info->columns[newPos].color++;
info->upperMapping[newPos] = newPos;
[currentLanes addObject: lane];
[lines addObject: [PBLine upperLineFrom: [currentLanes count] to: [currentLanes count]]];
[lines addObject: [PBLine lowerLineFrom: [currentLanes count] to: [currentLanes count]]];
}
}
// For existing columns, we always just continue straight down
info->lowerMapping[newPos] = newPos;
// ^^ I don't know what that means anymore :(
[lines addObject:[PBLine lowerLineFrom:newPos to:newPos]];
}
}
@@ -88,9 +82,9 @@
// If we already did the first parent, don't do so again
if (!didFirst) {
info->position = info->numColumns++;
info->columns[info->position].commit = [commit.parents objectAtIndex:0];
info->lowerMapping[info->position] = info->position;
[currentLanes addObject: [commit.parents objectAtIndex:0]];
newPos = [currentLanes count];
[lines addObject:[PBLine lowerLineFrom: newPos to: newPos]];
}
// Add all other parents
@@ -100,39 +94,41 @@
BOOL addedParent = NO;
for (NSString* parent in [commit.parents subarrayWithRange:NSMakeRange(1, [commit.parents count] -1)]) {
int i;
int i = 0;
BOOL was_displayed = NO;
for (i = 0; i < info->numColumns; i++)
if ([info->columns[i].commit isEqualToString: parent]) {
// TODO!
// !!! BUG
// This overwrites an existing mapping.
// We should instead have the possibility
// to add multiple lower mappings
// As we don't have that now, pieces of the graph are missing
info->lowerMapping[i] = info->position;
for (NSString* column in currentLanes) {
i++;
if ([column isEqualToString: parent]) {
[lines addObject:[PBLine lowerLineFrom: i to: newPos]];
was_displayed = YES;
break;
}
}
if (was_displayed)
continue;
// Really add this parent
addedParent = YES;
info->columns[info->numColumns++].commit = parent;
info->lowerMapping[info->numColumns -1] = info->position;
[currentLanes addObject:parent];
[lines addObject:[PBLine lowerLineFrom: [currentLanes count] to: newPos]];
}
// A parent was added, so we have room to not indent.
if (addedParent)
info->numColumns--;
previous = info;
++row;
previous = [[PBGraphCellInfo alloc] initWithPosition:newPos andLines:lines];
// If a parent was added, we have room to not indent.
if (addedParent)
previous.numColumns = [currentLanes count] - 1;
else
previous.numColumns = [currentLanes count];
previousLanes = currentLanes;
[cellsInfo addObject: previous];
}
}
- (struct PBGitGraphCellInfo) cellInfoForRow: (int) row
- (PBGraphCellInfo*) cellInfoForRow: (int) row
{
return cellsInfo[row];
return [cellsInfo objectAtIndex: row];
}
- (void) finalize
+2 -2
View File
@@ -10,9 +10,9 @@
#import "PBGitGrapher.h"
@interface PBGitRevisionCell : NSTextFieldCell {
PBGitCellInfo cellInfo;
PBGraphCellInfo* cellInfo;
BOOL isReady;
}
@property(assign) PBGitCellInfo cellInfo;
@property(assign) PBGraphCellInfo* cellInfo;
@end
+8 -11
View File
@@ -12,7 +12,7 @@
@implementation PBGitRevisionCell
@synthesize cellInfo;
-(void) setCellInfo: (PBGitCellInfo) info
-(void) setCellInfo: (PBGraphCellInfo*) info
{
isReady = YES;
cellInfo = info;
@@ -68,7 +68,7 @@
NSBezierPath * path = [NSBezierPath bezierPath];
path = [NSBezierPath bezierPathWithOvalInRect:oval];
[[col objectAtIndex:cellInfo.columns[c].color] set];
//[[col objectAtIndex:cellInfo.columns[c].color] set];
[path fill];
NSRect smallOval = { columnOrigin.x - 3, columnOrigin.y + r.size.height * 0.5 - 3, 6, 6};
@@ -90,16 +90,13 @@
// Adjust by removing the border
ownRect.size.height += 2;
ownRect.origin.y -= 1;
ownRect.origin.x += 10;
int column = 0;
ownRect.origin.x += 0;
// We can't iterate over numColumns here, as there may be connections to be drawn outside our columns.
for (column = 0; column < PBGitMaxColumns; column++) {
if (cellInfo.upperMapping[column] !=-1)
[self drawLineFromColumn:column toColumn: cellInfo.upperMapping[column] inRect:ownRect offset: 0];
if (cellInfo.lowerMapping[column] !=-1)
[self drawLineFromColumn: column toColumn: cellInfo.lowerMapping[column] inRect:ownRect offset: ownRect.size.height];
for (PBLine* line in cellInfo.lines) {
if (line.upper == 0)
[self drawLineFromColumn: line.from toColumn: line.to inRect:ownRect offset: ownRect.size.height];
else
[self drawLineFromColumn:line.from toColumn: line.to inRect:ownRect offset: 0];
}
[self drawCircleForColumn: cellInfo.position inRect: ownRect];
+23
View File
@@ -0,0 +1,23 @@
//
// PBGraphCellInfo.h
// GitX
//
// Created by Pieter de Bie on 27-08-08.
// Copyright 2008 __MyCompanyName__. All rights reserved.
//
#import <Cocoa/Cocoa.h>
@interface PBGraphCellInfo : NSObject
{
int position;
NSArray* lines;
int numColumns;
}
@property(readonly) NSArray* lines;
@property(assign) int position, numColumns;
- (id)initWithPosition: (int) p andLines: (NSArray*) l;
@end
+21
View File
@@ -0,0 +1,21 @@
//
// PBGraphCellInfo.m
// GitX
//
// Created by Pieter de Bie on 27-08-08.
// Copyright 2008 __MyCompanyName__. All rights reserved.
//
#import "PBGraphCellInfo.h"
@implementation PBGraphCellInfo
@synthesize lines, position, numColumns;
- (id)initWithPosition: (int) p andLines: (NSArray*) l
{
position = p;
lines = l;
return self;
}
@end
+23
View File
@@ -0,0 +1,23 @@
//
// PBLine.h
// GitX
//
// Created by Pieter de Bie on 27-08-08.
// Copyright 2008 __MyCompanyName__. All rights reserved.
//
#import <Cocoa/Cocoa.h>
@interface PBLine : NSObject
{
int upper;
int from;
int to;
}
@property(readonly) int upper, from, to;
- (id)initWithUpper: (int) u From: (int) f to: (int) t;
+ (PBLine*) lowerLineFrom:(int) f to: (int) t;
+ (PBLine*) upperLineFrom:(int) f to: (int) t;
@end
+32
View File
@@ -0,0 +1,32 @@
//
// PBLine.m
// GitX
//
// Created by Pieter de Bie on 27-08-08.
// Copyright 2008 __MyCompanyName__. All rights reserved.
//
#import "PBLine.h"
@implementation PBLine
@synthesize upper, from, to;
- (id)initWithUpper: (int) u From: (int) f to: (int) t;
{
upper = u;
from = f;
to = t;
return self;
}
+ (PBLine*) lowerLineFrom:(int) f to: (int) t
{
return [[PBLine alloc] initWithUpper:0 From:f to:t];
}
+ (PBLine*) upperLineFrom:(int) f to: (int) t
{
return [[PBLine alloc] initWithUpper:1 From:f to:t];
}
@end