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 "splineorder2.h"
31 : #include "splineutil.h"
32 : #include "splineutil2.h"
33 : #include <math.h>
34 :
35 : static struct shapedescrip {
36 : BasePoint me, prevcp, nextcp; int nocp;
37 : } ellipse3[] = {
38 : { { -1, 0 }, { -1, -0.552 }, { -1, 0.552 }, false },
39 : { { 0, 1 }, { -0.552, 1 }, { 0.552, 1 }, false },
40 : { { 1, 0 }, { 1, 0.552 }, { 1, -0.552 }, false },
41 : { { 0, -1 }, { 0.552, -1 }, { -0.552, -1 }, false },
42 : { { 0, 0 }, { 0, 0 }, { 0, 0 }, 0 }
43 : };
44 :
45 0 : static SplinePoint *SPMake(BasePoint *base,int pt) {
46 : SplinePoint *new;
47 :
48 0 : new = SplinePointCreate(base->x,base->y);
49 0 : new->pointtype = pt;
50 0 : new->selected = true;
51 0 : return( new );
52 : }
53 :
54 0 : static SplinePoint *SPMakeTo(BasePoint *base,int pt, SplinePoint *from) {
55 0 : SplinePoint *to = SPMake(base,pt);
56 0 : SplineMake(from,to,false); /* Always make a cubic, then convert to quadratic when done */
57 0 : return( to );
58 : }
59 :
60 0 : void CVMouseDownShape(CharView *cv,GEvent *event) {
61 0 : real radius = CVRoundRectRadius(); int points = CVPolyStarPoints();
62 : SplinePoint *last;
63 : int i;
64 : struct shapedescrip *ellipse;
65 :
66 0 : if ( event->u.mouse.clicks==2 &&
67 0 : (cv->active_tool == cvt_rect || cv->active_tool == cvt_elipse)) {
68 0 : CVRectEllipsePosDlg(cv);
69 0 : return;
70 : }
71 :
72 0 : CVClearSel(cv);
73 0 : CVPreserveState(&cv->b);
74 0 : CVSetCharChanged(cv,true);
75 0 : cv->active_shape = chunkalloc(sizeof(SplineSet));
76 0 : cv->active_shape->next = cv->b.layerheads[cv->b.drawmode]->splines;
77 0 : cv->b.layerheads[cv->b.drawmode]->splines = cv->active_shape;
78 0 : cv->active_shape->first = last = SPMake(&cv->info,pt_corner);
79 :
80 : /* always make a cubic shape. Then convert to order2 when done */
81 : /* the conversion routine already knows about rounding to int */
82 0 : switch ( cv->active_tool ) {
83 : case cvt_rect:
84 0 : if ( radius==0 ) {
85 0 : last = SPMakeTo(&cv->info,pt_corner,last);
86 0 : last = SPMakeTo(&cv->info,pt_corner,last);
87 0 : last = SPMakeTo(&cv->info,pt_corner,last);
88 : } else {
89 0 : last->pointtype = pt_tangent;
90 0 : last = SPMakeTo(&cv->info,pt_tangent,last);
91 0 : last = SPMakeTo(&cv->info,pt_tangent,last);
92 0 : last = SPMakeTo(&cv->info,pt_tangent,last);
93 0 : last = SPMakeTo(&cv->info,pt_tangent,last);
94 0 : last = SPMakeTo(&cv->info,pt_tangent,last);
95 0 : last = SPMakeTo(&cv->info,pt_tangent,last);
96 0 : last = SPMakeTo(&cv->info,pt_tangent,last);
97 : }
98 0 : break;
99 : case cvt_elipse:
100 0 : ellipse = /*order2 ? ellipse2 :*/ ellipse3;
101 0 : last->pointtype = pt_curve;
102 0 : for ( i=1; ellipse[i].me.x!=0 || ellipse[i].me.y!=0 ; ++i ) {
103 0 : last = SPMakeTo(&cv->info,pt_curve,last);
104 : }
105 0 : break;
106 : case cvt_poly:
107 0 : for ( i=1; i<points; ++i )
108 0 : last = SPMakeTo(&cv->info,pt_corner,last);
109 0 : break;
110 : case cvt_star:
111 0 : for ( i=1; i<2*points; ++i )
112 0 : last = SPMakeTo(&cv->info,pt_corner,last);
113 0 : break;
114 : }
115 0 : SplineMake(last,cv->active_shape->first,false);
116 0 : cv->active_shape->last = cv->active_shape->first;
117 0 : cv->b.sc->suspendMetricsViewEventPropagation = 1;
118 0 : SCUpdateAll(cv->b.sc);
119 : }
120 :
121 0 : static void SetCorner(SplinePoint *sp,real x, real y) {
122 0 : sp->me.x = x; sp->me.y = y;
123 0 : sp->nextcp = sp->me;
124 0 : sp->prevcp = sp->me;
125 0 : }
126 :
127 0 : static void SetCurve(SplinePoint *sp,BasePoint *center,real xrad, real yrad,
128 : struct shapedescrip *pt_d) {
129 0 : sp->me.x = center->x+xrad*pt_d->me.x; sp->me.y = center->y+yrad*pt_d->me.y;
130 0 : sp->nextcp.x = center->x+xrad*pt_d->nextcp.x; sp->nextcp.y = center->y+yrad*pt_d->nextcp.y;
131 0 : sp->prevcp.x = center->x+xrad*pt_d->prevcp.x; sp->prevcp.y = center->y+yrad*pt_d->prevcp.y;
132 0 : sp->nonextcp = sp->noprevcp = (xrad==0 && yrad==0);
133 0 : }
134 :
135 0 : static void SetPTangent(SplinePoint *sp,real x, real y,real xrad, real yrad) {
136 0 : sp->me.x = x; sp->me.y = y;
137 0 : sp->nextcp = sp->me;
138 0 : sp->prevcp = sp->me;
139 0 : if ( !sp->next->order2 ) {
140 0 : xrad *= .552;
141 0 : yrad *= .552;
142 : }
143 0 : sp->prevcp.x += xrad;
144 0 : sp->prevcp.y += yrad;
145 0 : sp->noprevcp = (xrad==0 && yrad==0);
146 0 : }
147 :
148 0 : static void SetNTangent(SplinePoint *sp,real x, real y,real xrad, real yrad) {
149 0 : sp->me.x = x; sp->me.y = y;
150 0 : sp->nextcp = sp->me;
151 0 : if ( !sp->next->order2 ) {
152 0 : xrad *= .552;
153 0 : yrad *= .552;
154 : }
155 0 : sp->nextcp.x += xrad;
156 0 : sp->nextcp.y += yrad;
157 0 : sp->prevcp = sp->me;
158 0 : sp->nonextcp = (xrad==0 && yrad==0);
159 0 : }
160 :
161 0 : static void RedoActiveSplineSet(SplineSet *ss) {
162 : Spline *spline, *first;
163 :
164 0 : first = NULL;
165 0 : for ( spline = ss->first->next; spline!=NULL && spline!=first; spline = spline->to->next ) {
166 0 : SplineRefigure(spline);
167 0 : if ( first == NULL ) first = spline;
168 : }
169 0 : }
170 :
171 0 : void CVMouseMoveShape(CharView *cv) {
172 0 : real radius = CVRoundRectRadius(); int points = CVPolyStarPoints();
173 0 : int center_out = CVRectElipseCenter();
174 : real xrad,yrad, xrr, yrr;
175 : real r2;
176 : SplinePoint *sp;
177 : real base, off;
178 : int i;
179 : struct shapedescrip *ellipse;
180 : BasePoint center;
181 :
182 0 : if ( cv->active_shape==NULL )
183 0 : return;
184 0 : switch ( cv->active_tool ) {
185 : case cvt_rect:
186 0 : if ( radius==0 ) {
187 0 : sp = cv->active_shape->first->next->to;
188 0 : if ( !center_out ) {
189 0 : SetCorner(sp,cv->p.cx,cv->info.y);
190 0 : SetCorner(sp=sp->next->to, cv->info.x,cv->info.y);
191 0 : SetCorner(sp=sp->next->to, cv->info.x,cv->p.cy);
192 : } else {
193 0 : if (( xrad = (cv->p.cx-cv->info.x) )<0 ) xrad = -xrad;
194 0 : if (( yrad = (cv->p.cy-cv->info.y) )<0 ) yrad = -yrad;
195 0 : SetCorner(sp,cv->p.cx-xrad,cv->p.cy+yrad);
196 0 : SetCorner(sp=sp->next->to, cv->p.cx+xrad,cv->p.cy+yrad);
197 0 : SetCorner(sp=sp->next->to, cv->p.cx+xrad,cv->p.cy-yrad);
198 0 : SetCorner(sp=sp->next->to, cv->p.cx-xrad,cv->p.cy-yrad);
199 : }
200 : } else {
201 0 : if ( !center_out ) {
202 0 : if (( xrad = (cv->p.cx-cv->info.x)/2 )<0 ) xrad = -xrad;
203 0 : if (( yrad = (cv->p.cy-cv->info.y)/2 )<0 ) yrad = -yrad;
204 0 : if ( xrad>radius ) xrr = radius; else xrr = xrad;
205 0 : if ( yrad>radius ) yrr = radius; else yrr = yrad;
206 0 : if ( cv->info.x<cv->p.cx ) xrr = -xrr;
207 0 : if ( cv->info.y<cv->p.cy ) yrr = -yrr;
208 0 : sp = cv->active_shape->first;
209 0 : SetPTangent(sp,
210 0 : cv->p.cx+xrr,cv->p.cy, -xrr,0);
211 0 : SetNTangent(sp=sp->next->to,
212 0 : cv->info.x-xrr,cv->p.cy, xrr,0);
213 0 : SetPTangent(sp=sp->next->to,
214 0 : cv->info.x,cv->p.cy+yrr, 0,-yrr);
215 0 : SetNTangent(sp=sp->next->to,
216 0 : cv->info.x,cv->info.y-yrr, 0,yrr);
217 0 : SetPTangent(sp=sp->next->to,
218 0 : cv->info.x-xrr,cv->info.y, xrr,0);
219 0 : SetNTangent(sp=sp->next->to,
220 0 : cv->p.cx+xrr,cv->info.y, -xrr,0);
221 0 : SetPTangent(sp=sp->next->to,
222 0 : cv->p.cx,cv->info.y-yrr, 0,yrr);
223 0 : SetNTangent(sp=sp->next->to,
224 0 : cv->p.cx,cv->p.cy+yrr, 0,-yrr);
225 : } else {
226 0 : if (( xrad = (cv->p.cx-cv->info.x) )<0 ) xrad = -xrad;
227 0 : if (( yrad = (cv->p.cy-cv->info.y) )<0 ) yrad = -yrad;
228 0 : if ( xrad>radius ) xrr = radius; else xrr = xrad;
229 0 : if ( yrad>radius ) yrr = radius; else yrr = yrad;
230 0 : sp = cv->active_shape->first;
231 0 : SetPTangent(sp,
232 0 : cv->p.cx-xrad+xrr,cv->p.cy-yrad, -xrr,0);
233 0 : SetNTangent(sp=sp->next->to,
234 0 : cv->p.cx+xrad-xrr,cv->p.cy-yrad, xrr,0);
235 0 : SetPTangent(sp=sp->next->to,
236 0 : cv->p.cx+xrad,cv->p.cy-yrad+yrr, 0,-yrr);
237 0 : SetNTangent(sp=sp->next->to,
238 0 : cv->p.cx+xrad,cv->p.cy+yrad-yrr, 0,yrr);
239 0 : SetPTangent(sp=sp->next->to,
240 0 : cv->p.cx+xrad-xrr,cv->p.cy+yrad, xrr,0);
241 0 : SetNTangent(sp=sp->next->to,
242 0 : cv->p.cx-xrad+xrr,cv->p.cy+yrad, -xrr,0);
243 0 : SetPTangent(sp=sp->next->to,
244 0 : cv->p.cx-xrad,cv->p.cy+yrad-yrr, 0,yrr);
245 0 : SetNTangent(sp=sp->next->to,
246 0 : cv->p.cx-xrad,cv->p.cy-yrad+yrr, 0,-yrr);
247 : }
248 : }
249 0 : break;
250 : case cvt_elipse:
251 0 : if ( !center_out ) {
252 0 : center.x = (cv->p.cx+cv->info.x)/2; center.y = (cv->p.cy+cv->info.y)/2;
253 0 : if (( xrad = (cv->p.cx-cv->info.x)/2 )<0 ) xrad = -xrad;
254 0 : if (( yrad = (cv->p.cy-cv->info.y)/2 )<0 ) yrad = -yrad;
255 : } else {
256 0 : center.x = cv->p.cx; center.y = cv->p.cy;
257 0 : if (( xrad = (cv->p.cx-cv->info.x) )<0 ) xrad = -xrad;
258 0 : if (( yrad = (cv->p.cy-cv->info.y) )<0 ) yrad = -yrad;
259 : }
260 0 : if ( cv->b.layerheads[cv->b.drawmode]->order2 ) {
261 0 : xrad = rint(xrad);
262 0 : yrad = rint(yrad);
263 : }
264 0 : ellipse = /*cv->b.sc->parent->order2 ? ellipse2 :*/ ellipse3;
265 0 : for ( i=0, sp=cv->active_shape->first; ellipse[i].me.x!=0 || ellipse[i].me.y!=0 ; ++i, sp=sp->next->to )
266 0 : SetCurve(sp,¢er,xrad,yrad,&ellipse[i]);
267 0 : break;
268 : case cvt_poly:
269 0 : base = atan2(cv->p.cy-cv->info.y,cv->p.cx-cv->info.x);
270 0 : radius = sqrt((cv->p.cy-cv->info.y)*(cv->p.cy-cv->info.y) +
271 0 : (cv->p.cx-cv->info.x)*(cv->p.cx-cv->info.x));
272 0 : off = -2*3.1415926535897932/points;
273 0 : sp = cv->active_shape->last->prev->from;
274 0 : for ( i=0; i<points; ++i )
275 0 : SetCorner(sp=sp->next->to, cv->p.cx+radius*cos(base+i*off),
276 0 : cv->p.cy+radius*sin(base+i*off));
277 0 : break;
278 : case cvt_star:
279 0 : base = atan2(cv->p.cy-cv->info.y,cv->p.cx-cv->info.x)-3.1415926535897932;
280 0 : if ( base<-3.1416 )
281 0 : base += 2*3.1415926535897932;
282 0 : radius = sqrt((cv->p.cy-cv->info.y)*(cv->p.cy-cv->info.y) +
283 0 : (cv->p.cx-cv->info.x)*(cv->p.cx-cv->info.x));
284 0 : off = -2*3.1415926535897932/(2*points);
285 0 : sp = cv->active_shape->last->prev->from;
286 0 : r2 = radius/CVStarRatio();
287 0 : for ( i=0; i<2*points; ++i ) {
288 0 : if ( i&1 ) {
289 0 : SetCorner(sp=sp->next->to, cv->p.cx+r2*cos(base+i*off),
290 0 : cv->p.cy+r2*sin(base+i*off));
291 : } else {
292 0 : SetCorner(sp=sp->next->to, cv->p.cx+radius*cos(base+i*off),
293 0 : cv->p.cy+radius*sin(base+i*off));
294 : }
295 : }
296 0 : break;
297 : }
298 0 : RedoActiveSplineSet(cv->active_shape);
299 0 : cv->b.sc->suspendMetricsViewEventPropagation = 1;
300 0 : SCUpdateAll(cv->b.sc);
301 : }
302 :
303 0 : void CVMouseUpShape(CharView *cv) {
304 : SplinePoint *first, *second, *sp;
305 : extern int snaptoint;
306 :
307 0 : if ( cv->active_shape==NULL )
308 0 : return;
309 :
310 0 : if ( cv->b.layerheads[cv->b.drawmode]->order2 ) {
311 : SplineSet *prev, *new, *ss;
312 0 : new = SplineSetsTTFApprox(cv->active_shape);
313 0 : for ( ss=cv->b.layerheads[cv->b.drawmode]->splines, prev=NULL;
314 0 : ss!=NULL && ss!=cv->active_shape;
315 0 : prev = ss, ss=ss->next );
316 0 : if ( ss==NULL )
317 0 : IError("Couldn't find shape");
318 : else {
319 0 : if ( prev==NULL )
320 0 : cv->b.layerheads[cv->b.drawmode]->splines = new;
321 : else
322 0 : prev->next = new;
323 0 : SplinePointListsFree(cv->active_shape);
324 0 : cv->active_shape = new;
325 : }
326 : }
327 0 : first = cv->active_shape->first; second = first->next->to;
328 0 : if ( first->me.x == second->me.x && first->me.y == second->me.y ) {
329 : /* Remove this shape, it will be selected */
330 0 : cv->b.layerheads[cv->b.drawmode]->splines = SplinePointListRemoveSelected(cv->b.sc,
331 0 : cv->b.layerheads[cv->b.drawmode]->splines);
332 0 : } else if ( cv->active_tool==cvt_rect || cv->active_tool==cvt_elipse ) {
333 0 : if ( SplinePointListIsClockwise(cv->active_shape)==0 )
334 0 : SplineSetReverse(cv->active_shape);
335 0 : if ( snaptoint ) {
336 0 : for ( sp= cv->active_shape->first; ; ) {
337 0 : SplinePointRound(sp,1.0);
338 0 : sp = sp->next->to;
339 0 : if ( sp==cv->active_shape->first )
340 0 : break;
341 0 : }
342 0 : for ( sp= cv->active_shape->first; ; ) {
343 0 : SplineRefigure(sp->next);
344 0 : sp = sp->next->to;
345 0 : if ( sp==cv->active_shape->first )
346 0 : break;
347 0 : }
348 : }
349 : }
350 0 : if ( cv->b.sc->inspiro && hasspiro()) {
351 0 : free(cv->active_shape->spiros);
352 0 : cv->active_shape->spiros = SplineSet2SpiroCP(cv->active_shape,&cv->active_shape->spiro_cnt);
353 0 : cv->active_shape->spiro_max = cv->active_shape->spiro_cnt;
354 : }
355 0 : cv->active_shape = NULL;
356 :
357 0 : cv->b.sc->suspendMetricsViewEventPropagation = 0;
358 0 : SCUpdateAll(cv->b.sc);
359 : }
|