Line data Source code
1 : /* Copyright (C) 2000-2012 by George Williams */
2 : /*
3 : * Redistribution and use in source and binary forms, with or without
4 : * modification, are permitted provided that the following conditions are met:
5 :
6 : * Redistributions of source code must retain the above copyright notice, this
7 : * list of conditions and the following disclaimer.
8 :
9 : * Redistributions in binary form must reproduce the above copyright notice,
10 : * this list of conditions and the following disclaimer in the documentation
11 : * and/or other materials provided with the distribution.
12 :
13 : * The name of the author may not be used to endorse or promote products
14 : * derived from this software without specific prior written permission.
15 :
16 : * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17 : * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18 : * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19 : * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20 : * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 : * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22 : * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23 : * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24 : * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25 : * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 : */
27 : #include "cvundoes.h"
28 : #include "fontforgeui.h"
29 : #include "spiro.h"
30 : #include "collabclientui.h"
31 : #include "splineutil.h"
32 : #include <math.h>
33 :
34 : #if defined(KNIFE_CONTINUOUS) /* Use this code to do cuts as we move along. Probably a bad idea, let's wait till the end */
35 : static void ProcessKnife(CharView *cv, PressedOn *p) {
36 : real dx, dy;
37 : SplinePoint *n;
38 :
39 : /* If we've already made a cut, don't make another cut too close to it */
40 : /* ie. if the hand shakes over a cut let's not get about six tiny cut */
41 : /* segments adjacent to one another */
42 : if ( (dx=cv->info.x-cv->lastknife.x)<0 ) dx=-dx;
43 : if ( (dy=cv->info.y-cv->lastknife.y)<0 ) dy=-dy;
44 : if ( (dx+dy)/cv->scale <= 6 )
45 : return;
46 : if ( p->sp==NULL && p->spline==NULL )
47 : return; /* Nothing to cut */
48 :
49 : if ( p->spline!=NULL )
50 : p->sp = SplineBisect(p->spline,p->t);
51 : if ( p->spl==NULL ) /* Kanou says this can happen. It doesn't hurt to check for it */
52 : return;
53 : if ( p->spl->first!=p->spl->last )
54 : if ( p->sp==p->spl->first || p->sp==p->spl->last )
55 : return; /* Already cut here */
56 : n = chunkalloc(sizeof(SplinePoint));
57 : p->sp->pointtype = pt_corner;
58 : *n = *p->sp;
59 : n->hintmask = NULL;
60 : p->sp->next = NULL;
61 : n->prev = NULL;
62 : n->next->from = n;
63 : if ( p->spl->first==p->spl->last ) {
64 : p->spl->first = n;
65 : p->spl->last = p->sp;
66 : } else {
67 : SplinePointList *nspl = chunkalloc(sizeof(SplinePointList));
68 : nspl->next = p->spl->next;
69 : p->spl->next = nspl;
70 : nspl->first = n;
71 : nspl->last = p->spl->last;
72 : p->spl->last = p->sp;
73 : }
74 :
75 : cv->lastknife.x = cv->info.x;
76 : cv->lastknife.y = cv->info.y;
77 : CVSetCharChanged(cv,true);
78 : SCUpdateAll(cv->b.sc);
79 : }
80 : #endif
81 :
82 0 : void CVMouseDownKnife(CharView *cv) {
83 : #if defined(KNIFE_CONTINUOUS)
84 : CVPreserveState(&cv->b);
85 : cv->lastknife.x = cv->lastknife.y = -9999;
86 : ProcessKnife(cv,&cv->p);
87 : #else
88 0 : cv->p.rubberlining = true;
89 : #endif
90 0 : }
91 :
92 0 : void CVMouseMoveKnife(CharView *cv, PressedOn *p) {
93 : #if defined(KNIFE_CONTINUOUS)
94 : ProcessKnife(cv,p);
95 : #else
96 0 : GDrawRequestExpose(cv->v,NULL,false);
97 : #endif
98 0 : }
99 :
100 : #if !defined(KNIFE_CONTINUOUS)
101 0 : static void ReorderSpirosAndAddAndCut(SplineSet *spl,int spiro_index) {
102 : /* We just cut a closed contour. It is now open. */
103 : /* If spl->spiros[spiro_index] == spl->first->me then they cut on top of */
104 : /* a point which already existed */
105 : /* If so, move that point to the head of the list */
106 : /* copy it so that it's at the end of the list too */
107 : /* mark the start as SPIRO_OPEN */
108 : /* Otherwise they cut between spiro_cps */
109 : /* Insert 2 copies of spl->first->me after spiro_index */
110 : /* rotate so that one of these is at the end of the list and the other at the start */
111 : /* mark start as SPIRO_OPEN */
112 : spiro_cp *newspiros;
113 :
114 0 : if ( spiro_index!=spl->spiro_cnt-1 &&
115 0 : spl->first->me.x == spl->spiros[spiro_index].x &&
116 0 : spl->first->me.y == spl->spiros[spiro_index].y ) {
117 0 : newspiros = malloc((spl->spiro_cnt+1) * sizeof(spiro_cp));
118 0 : memcpy(newspiros,spl->spiros+spiro_index,(spl->spiro_cnt-1-spiro_index)*sizeof(spiro_cp));
119 0 : memcpy(newspiros+(spl->spiro_cnt-1-spiro_index),spl->spiros,spiro_index*sizeof(spiro_cp));
120 0 : memcpy(newspiros+spl->spiro_cnt-1,newspiros,sizeof(spiro_cp));
121 0 : memcpy(newspiros+spl->spiro_cnt,spl->spiros+spl->spiro_cnt-1,sizeof(spiro_cp));
122 0 : newspiros[0].ty = SPIRO_OPEN_CONTOUR;
123 0 : free(spl->spiros);
124 0 : spl->spiros = newspiros;
125 0 : ++(spl->spiro_cnt);
126 0 : spl->spiro_max = spl->spiro_cnt;
127 : } else {
128 0 : newspiros = malloc((spl->spiro_cnt+2) * sizeof(spiro_cp));
129 0 : newspiros[0].x = spl->first->me.x;
130 0 : newspiros[0].y = spl->first->me.y;
131 0 : newspiros[0].ty = SPIRO_OPEN_CONTOUR;
132 0 : memcpy(newspiros+1,spl->spiros+spiro_index+1,(spl->spiro_cnt-1-(spiro_index+1))*sizeof(spiro_cp));
133 0 : memcpy(newspiros+1+(spl->spiro_cnt-1-(spiro_index+1)),spl->spiros,(spiro_index+1)*sizeof(spiro_cp));
134 0 : memcpy(newspiros+spl->spiro_cnt,newspiros,sizeof(spiro_cp));
135 0 : memcpy(newspiros+spl->spiro_cnt+1,spl->spiros+spl->spiro_cnt-1,sizeof(spiro_cp));
136 0 : newspiros[spl->spiro_cnt].ty = SPIRO_G4;
137 0 : free(spl->spiros);
138 0 : spl->spiros = newspiros;
139 0 : spl->spiro_cnt += 2;
140 0 : spl->spiro_max = spl->spiro_cnt;
141 : }
142 0 : }
143 :
144 0 : static void SplitSpirosAndAddAndCut(SplineSet *spl,SplineSet *spl2,int spiro_index) {
145 : /* OK, spl was an open contour and we just cut it at spiro_index */
146 : /* We don't need to rotate the spiros */
147 : /* The first half of spl remains in it. (We'll leave all spiros up to and including spiro_index) */
148 : /* If spl->spiros[spiro_index] != spl->first->me we must add spl->first here */
149 : /* In the spl2 we either start out with spl->spiros[spiro_index] or with spl->first */
150 : /* then add all spiros after spiro_index */
151 :
152 0 : spl2->spiros = malloc((spl->spiro_cnt-spiro_index+2) * sizeof(spiro_cp));
153 0 : spl2->spiro_max = spl->spiro_cnt-spiro_index+2;
154 0 : if ( spl2->first->me.x == spl->spiros[spiro_index].x &&
155 0 : spl2->first->me.y == spl->spiros[spiro_index].y ) {
156 0 : memcpy(spl2->spiros,spl->spiros+spiro_index,(spl->spiro_cnt-spiro_index)*sizeof(spiro_cp));
157 0 : spl2->spiros[0].ty = SPIRO_OPEN_CONTOUR;
158 0 : memcpy(spl->spiros+spiro_index+1,spl->spiros+spl->spiro_cnt-1,sizeof(spiro_cp));
159 0 : spl2->spiro_cnt = spl->spiro_cnt-spiro_index;
160 0 : spl->spiro_cnt = spiro_index+2;
161 : } else {
162 0 : spl2->spiros[0].x = spl2->first->me.x;
163 0 : spl2->spiros[0].y = spl2->first->me.y;
164 0 : spl2->spiros[0].ty = SPIRO_OPEN_CONTOUR;
165 0 : memcpy(spl2->spiros+1,spl->spiros+spiro_index+1,(spl->spiro_cnt-(spiro_index+1))*sizeof(spiro_cp));
166 0 : spl2->spiro_cnt = spl->spiro_cnt-spiro_index;
167 0 : if ( spiro_index+3>spl->spiro_max )
168 0 : spl->spiros = realloc(spl->spiros,(spl->spiro_max=spiro_index+3)*sizeof(spiro_cp));
169 0 : memcpy(spl->spiros+spiro_index+1,spl2->spiros,sizeof(spiro_cp));
170 0 : spl->spiros[spiro_index+1].ty = SPIRO_G4;
171 0 : memcpy(spl->spiros+spiro_index+2,spl->spiros+spl->spiro_cnt-1,sizeof(spiro_cp));
172 0 : spl->spiro_cnt = spiro_index+3;
173 : }
174 0 : }
175 : #endif
176 :
177 0 : void CVMouseUpKnife(CharView *cv, GEvent *event)
178 : {
179 : #if !defined(KNIFE_CONTINUOUS)
180 : /* draw a line from (cv->p.cx,cv->p.cy) to (cv->info.x,cv->info.y) */
181 : /* and cut anything intersected by it */
182 : SplineSet *spl, *spl2;
183 : Spline *s, *nexts;
184 : Spline dummy;
185 : SplinePoint dummyfrom, dummyto, *mid, *mid2;
186 : BasePoint inters[9];
187 : extended t1s[10], t2s[10];
188 0 : int foundsomething = true, ever = false;
189 : int i;
190 0 : int spiro_index = 0;
191 :
192 0 : memset(&dummy,0,sizeof(dummy));
193 0 : memset(&dummyfrom,0,sizeof(dummyfrom));
194 0 : memset(&dummyto,0,sizeof(dummyto));
195 0 : dummyfrom.me.x = cv->p.cx; dummyfrom.me.y = cv->p.cy;
196 0 : dummyto.me.x = cv->info.x; dummyto.me.y = cv->info.y;
197 0 : dummyfrom.nextcp = dummyfrom.prevcp = dummyfrom.me;
198 0 : dummyto.nextcp = dummyto.prevcp = dummyto.me;
199 0 : dummyfrom.nonextcp = dummyfrom.noprevcp = dummyto.nonextcp = dummyto.noprevcp = true;
200 0 : dummy.splines[0].d = cv->p.cx; dummy.splines[0].c = cv->info.x-cv->p.cx;
201 0 : dummy.splines[1].d = cv->p.cy; dummy.splines[1].c = cv->info.y-cv->p.cy;
202 0 : dummy.from = &dummyfrom; dummy.to = &dummyto;
203 0 : dummy.islinear = dummy.knownlinear = true;
204 0 : dummyfrom.next = dummyto.prev = &dummy;
205 :
206 0 : for ( spl = cv->b.layerheads[cv->b.drawmode]->splines; spl!=NULL ; spl = spl->next )
207 0 : spl->ticked = false;
208 :
209 0 : while ( foundsomething ) {
210 0 : foundsomething = false;
211 0 : for ( spl = cv->b.layerheads[cv->b.drawmode]->splines; spl!=NULL && !foundsomething; spl = spl->next ) {
212 0 : for ( s = spl->first->next; s!=NULL ; ) {
213 0 : nexts = NULL;
214 0 : if ( s->to!=spl->first )
215 0 : nexts = s->to->next;
216 0 : if ( SplinesIntersect(s,&dummy,inters,t1s,t2s)>0 ) {
217 0 : if ( event->u.mouse.state&ksm_meta ) {
218 0 : for ( i=0; i<4 && t1s[i]!=-1 && (t1s[i]<.0001 || t1s[i]>1-.0001); ++i );
219 0 : if ( i<4 && t1s[i]!=-1 ) {
220 : /* With meta down we just remove the spline rather than */
221 : /* cutting it */
222 0 : foundsomething = true;
223 0 : nexts = NULL;
224 0 : if ( !ever )
225 0 : CVPreserveState(&cv->b);
226 0 : ever = true;
227 0 : if ( spl->first==spl->last ) {
228 0 : spl->first = s->to;
229 0 : spl->last = s->from;
230 : } else {
231 0 : spl2 = chunkalloc(sizeof(SplineSet));
232 0 : spl2->next = spl->next;
233 0 : spl->next = spl2;
234 0 : spl2->first = s->to;
235 0 : spl2->last = spl->last;
236 0 : spl->last = s->from;
237 : }
238 0 : s->to->prev = s->from->next = NULL;
239 0 : SplineFree(s);
240 0 : SplineSetSpirosClear(spl);
241 : }
242 : } else {
243 0 : for ( i=0; i<4 && t1s[i]!=-1 &&
244 0 : (( t1s[i]<.001 && s->from->prev==NULL ) ||
245 0 : ( t1s[i]>1-.001 && s->to->next == NULL ));
246 0 : ++i );
247 0 : if ( i<4 && t1s[i]!=-1 ) {
248 0 : foundsomething = true;
249 0 : nexts = NULL;
250 0 : if ( !ever )
251 0 : CVPreserveState(&cv->b);
252 0 : ever = true;
253 0 : spiro_index = -1;
254 0 : if ( cv->b.sc->inspiro && hasspiro()) {
255 0 : if ( spl->spiro_cnt!=0 )
256 0 : spiro_index = SplineT2SpiroIndex(s,t1s[i],spl);
257 : } else
258 0 : SplineSetSpirosClear(spl);
259 0 : if ( t1s[i]<.001 ) {
260 0 : mid = s->from;
261 0 : } else if ( t1s[i]>1-.001 ) {
262 0 : mid = s->to;
263 : } else
264 0 : mid = SplineBisect(s,t1s[i]);
265 : /* if the intersection is close to an end point */
266 : /* cut at the end point, else break in the middle */
267 : /* Cut here, and then */
268 : /* start all over again (we may need to alter the */
269 : /* splineset structure so drastically that we just */
270 : /* can't continue these loops) */
271 0 : mid->pointtype = pt_corner;
272 0 : mid2 = chunkalloc(sizeof(SplinePoint));
273 0 : *mid2 = *mid;
274 0 : mid2->hintmask = NULL;
275 0 : mid->next = NULL;
276 0 : mid2->prev = NULL;
277 0 : mid2->next->from = mid2;
278 0 : spl->ticked = true;
279 0 : if ( spl->first==spl->last ) {
280 0 : spl->first = mid2;
281 0 : spl->last = mid;
282 0 : if ( spiro_index!=-1 )
283 0 : ReorderSpirosAndAddAndCut(spl,spiro_index);
284 : } else {
285 0 : spl2 = chunkalloc(sizeof(SplineSet));
286 0 : spl2->next = spl->next;
287 0 : spl->next = spl2;
288 0 : spl2->first = mid2;
289 0 : spl2->last = spl->last;
290 0 : spl->last = mid;
291 0 : if ( spiro_index!=-1 )
292 0 : SplitSpirosAndAddAndCut(spl,spl2,spiro_index);
293 0 : spl2->ticked = true;
294 : }
295 : }
296 : }
297 : }
298 0 : s = nexts;
299 : }
300 : }
301 : }
302 0 : if ( ever ) {
303 0 : for ( spl = cv->b.layerheads[cv->b.drawmode]->splines; spl!=NULL ; spl = spl->next ) {
304 0 : if ( spl->ticked && spl->spiros!=NULL && cv->b.sc->inspiro && hasspiro())
305 0 : SSRegenerateFromSpiros(spl);
306 0 : spl->ticked = false;
307 : }
308 0 : CVCharChangedUpdate( &cv->b );
309 0 : collabclient_sendRedo( &cv->b );
310 : }
311 : #endif
312 0 : }
313 :
|