summaryrefslogtreecommitdiff
path: root/tikzit/src/gtk/Edge+Render.m
blob: 1f5c41a364396b7e5f6214e789f25231d824d0d8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
/*
 * Copyright 2011  Alex Merry <dev@randomguy3.me.uk>
 * Copyright 2010  Chris Heunen
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of
 * the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#import "Edge+Render.h"
#import "Node+Render.h"
#import "../common/util.h"

static const float edgeWidth = 2.0;
static const float cpRadius = 3.0;
static const float cpLineWidth = 1.0;

@implementation Edge (Render)

+ (float) controlPointRadius {
    return cpRadius;
}

- (float) controlDistanceWithTransformer:(Transformer*)transformer {
    NSPoint c_source = [transformer toScreen:src];
    NSPoint c_target = [transformer toScreen:targ];
    const float dx = (c_target.x - c_source.x);
    const float dy = (c_target.y - c_source.y);
    if (dx == 0 && dy == 0) {
        return [transformer scaleToScreen:weight];
    } else {
        return NSDistanceBetweenPoints(c_source, c_target) * weight;
    }
}

- (void) renderControlsInContext:(id<RenderContext>)context withTransformer:(Transformer*)transformer {
    [context saveState];

    [self updateControls];

    NSPoint c_source = [transformer toScreen:src];
    NSPoint c_target = [transformer toScreen:targ];
    NSPoint c_mid = [transformer toScreen:mid];

    const float dx = (c_target.x - c_source.x);
    const float dy = (c_target.y - c_source.y);

    [context setLineWidth:cpLineWidth];
    RColor fillColor = MakeRColor (1.0, 1.0, 1.0, 0.5);

    // draw a circle at the mid point
    [context startPath];
    [context circleAt:c_mid withRadius:cpRadius];
    [context strokePathWithColor:MakeSolidRColor(0, 0, 1) andFillWithColor:fillColor];

    //[context setAntialiasMode:AntialiasDisabled];

    // size of control circles
    float c_dist = 0.0f;
    if (dx == 0 && dy == 0) {
        c_dist = [transformer scaleToScreen:weight];
    } else {
        c_dist = NSDistanceBetweenPoints(c_source, c_target) * weight;
    }

    // basic bend is blue, in-out is green
    RColor controlTrackColor;
    if ([self bendMode] == EdgeBendModeBasic) {
        controlTrackColor = MakeRColor (0.0, 0.0, 1.0, 0.4);
    } else {
        controlTrackColor = MakeRColor (0.0, 0.7, 0.0, 0.4);
    }

    [context startPath];
    [context circleAt:c_source withRadius:c_dist];
    if (dx != 0 || dy != 0) {
        [context circleAt:c_target withRadius:c_dist];
    }
    [context strokePathWithColor:controlTrackColor];

    RColor handleColor = MakeRColor (1.0, 0.0, 1.0, 0.6);
    if ([self bendMode] == EdgeBendModeBasic) {
        if (bend % 45 != 0) {
            handleColor = MakeRColor (0.0, 0.0, 0.1, 0.4);
        }
    } else if ([self bendMode] == EdgeBendModeInOut) {
        if (outAngle % 45 != 0) {
            handleColor = MakeRColor (0.0, 0.7, 0.0, 0.4);
        }
    }

    NSPoint c_cp1 = [transformer toScreen:cp1];
    [context moveTo:c_source];
    [context lineTo:c_cp1];
    [context circleAt:c_cp1 withRadius:cpRadius];
    [context strokePathWithColor:handleColor];

    if ([self bendMode] == EdgeBendModeInOut) {
        // recalculate color based on inAngle
        if (inAngle % 45 == 0) {
            handleColor = MakeRColor (1.0, 0.0, 1.0, 0.6);
        } else {
            handleColor = MakeRColor (0.0, 0.7, 0.0, 0.4);
        }
    }

    NSPoint c_cp2 = [transformer toScreen:cp2];
    [context moveTo:c_target];
    [context lineTo:c_cp2];
    [context circleAt:c_cp2 withRadius:cpRadius];
    [context strokePathWithColor:handleColor];

    [context restoreState];
}

- (void) renderArrowStrokePathInContext:(id<RenderContext>)context withTransformer:(Transformer*)transformer color:(RColor)color {

    if ([self style] != nil) {
        switch ([[self style] headStyle]) {
            case AH_None:
                break;
            case AH_Plain:
                [context startPath];
                [context moveTo:[transformer toScreen:[self leftHeadNormal]]];
                [context lineTo:[transformer toScreen:head]];
                [context lineTo:[transformer toScreen:[self rightHeadNormal]]];
                [context strokePathWithColor:color];
                break;
            case AH_Latex:
                [context startPath];
                [context moveTo:[transformer toScreen:[self leftHeadNormal]]];
                [context lineTo:[transformer toScreen:head]];
                [context lineTo:[transformer toScreen:[self rightHeadNormal]]];
                [context closeSubPath];
                [context strokePathWithColor:color andFillWithColor:color];
                break;
        }
        switch ([[self style] tailStyle]) {
            case AH_None:
                break;
            case AH_Plain:
                [context startPath];
                [context moveTo:[transformer toScreen:[self leftTailNormal]]];
                [context lineTo:[transformer toScreen:tail]];
                [context lineTo:[transformer toScreen:[self rightTailNormal]]];
                [context strokePathWithColor:color];
                break;
            case AH_Latex:
                [context startPath];
                [context moveTo:[transformer toScreen:[self leftTailNormal]]];
                [context lineTo:[transformer toScreen:tail]];
                [context lineTo:[transformer toScreen:[self rightTailNormal]]];
                [context closeSubPath];
                [context strokePathWithColor:color andFillWithColor:color];
                break;
        }
    }
}

- (void) createStrokePathInContext:(id<RenderContext>)context withTransformer:(Transformer*)transformer {
    NSPoint c_tail = [transformer toScreen:tail];
    NSPoint c_cp1 = [transformer toScreen:cp1];
    NSPoint c_cp2 = [transformer toScreen:cp2];
    NSPoint c_head = [transformer toScreen:head];

    [context startPath];
    [context moveTo:c_tail];
    [context curveTo:c_head withCp1:c_cp1 andCp2:c_cp2];

    if ([self style] != nil) {
        // draw edge decoration
        switch ([[self style] decorationStyle]) {
            case ED_None:
                break;
            case ED_Tick:
                [context moveTo:[transformer toScreen:[self leftNormal]]];
                [context lineTo:[transformer toScreen:[self rightNormal]]];
                break;
            case ED_Arrow:
                [context moveTo:[transformer toScreen:[self leftNormal]]];
                [context lineTo:[transformer toScreen:[self midTan]]];
                [context lineTo:[transformer toScreen:[self rightNormal]]];
                break;
        }

    }
}

- (RColor) color {
    if (style) {
        return [[style colorRGB] rColor];
    } else {
        return BlackRColor;
    }
}

- (void) renderBasicEdgeInContext:(id<RenderContext>)context withTransformer:(Transformer*)t selected:(BOOL)selected {
    [self updateControls];
    [context saveState];

    const CGFloat lineWidth = style ? [style thickness] : edgeWidth;
    [context setLineWidth:lineWidth];
    RColor color = [self color];
    if (selected) {
        color.alpha = 0.5;
    }

    [self createStrokePathInContext:context withTransformer:t];
    [context strokePathWithColor:color];

    [self renderArrowStrokePathInContext:context withTransformer:t color:color];

    [context restoreState];
}

- (void) renderToSurface:(id <Surface>)surface withContext:(id<RenderContext>)context selected:(BOOL)selected {
    [self renderBasicEdgeInContext:context withTransformer:[surface transformer] selected:selected];

    if (selected) {
        [self renderControlsInContext:context withTransformer:[surface transformer]];
    }

    if ([self hasEdgeNode]) {
        NSPoint labelPt = [[surface transformer] toScreen:[self mid]];
        [[self edgeNode] renderLabelAt:labelPt
                           withContext:context];
    }
}

- (NSRect) renderedBoundsWithTransformer:(Transformer*)t whenSelected:(BOOL)selected {
    NSRect bRect = [t rectToScreen:[self boundingRect]];
    if (selected) {
        float c_dist = [self controlDistanceWithTransformer:t];
        return NSInsetRect (bRect, -c_dist, -c_dist);
    } else {
        return bRect;
    }
}

- (BOOL) hitByPoint:(NSPoint)p onSurface:(id<Surface>)surface withFuzz:(float)fuzz {
    [self updateControls];

    NSRect boundingRect = [[surface transformer] rectToScreen:[self boundingRect]];
    if (!NSPointInRect(p, NSInsetRect(boundingRect, -fuzz, -fuzz))) {
        return NO;
    }

    id<RenderContext> cr = [surface createRenderContext];

    [cr setLineWidth:edgeWidth + 2 * fuzz];
    [self createStrokePathInContext:cr withTransformer:[surface transformer]];

    return [cr strokeIncludesPoint:p];
}

@end

// vim:ft=objc:ts=4:et:sts=4:sw=4