1(* 2 Copyright 2006,2010,2011 by Mark Weyer 3 4 This program is free software; you can redistribute it and/or modify 5 it under the terms of the GNU General Public License as published by 6 the Free Software Foundation; either version 2 of the License, or 7 (at your option) any later version. 8 9 This program is distributed in the hope that it will be useful, 10 but WITHOUT ANY WARRANTY; without even the implied warranty of 11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 GNU General Public License for more details. 13 14 You should have received a copy of the GNU General Public License 15 along with this program; if not, write to the Free Software 16 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 17*) 18 19let eps = 0.000001 20 (* Diese N�he (in der Parametrisierung von 0 bis 1) zum Endpunkt einer 21 Strecke reicht f�r Gleichheit. *) 22 23let laengen_fehler = 1.0001 24 (* Sobald die Dreiecksungleichung h�chstens um diesen Faktor unscharf ist, 25 wird bei der L�ngenberechnung nicht noch weiter aufgeteilt. *) 26 27open Farbe 28open Graphik 29open Helfer 30 31(* 32 Zuerst ein paar Hilfsdefinitionen 33*) 34 35type 'a lazyval = Unknown of (unit -> 'a) | Known of 'a 36 37let set_lazy f = ref (Unknown f) 38let get_lazy l = match (!l) with 39 | Unknown f -> let v=f () in (l:=Known v; v) 40 | Known v -> v 41 42let xor a b = (a || b) && (not (a && b)) 43 44let list_min f (h::t) = List.fold_left 45 (function bisher -> function x -> min bisher (f x)) 46 (f h) 47 t 48let list_max f (h::t) = List.fold_left 49 (function bisher -> function x -> max bisher (f x)) 50 (f h) 51 t 52let list_sum f = List.fold_left 53 (function bisher -> function x -> bisher+.(f x)) 54 0.0 55 56let skalar (x0,y0) (x,y) (x',y') = (x-.x0)*.(x'-.x0) +. (y-.y0)*.(y'-.y0) 57let abstand p1 p2 = sqrt (skalar p1 p2 p2) 58let richtung (x1,y1) (x2,y2) = atan2 (y2-.y1) (x2-.x1) 59 (* Achtung: Die Punkte m�ssen verschieden sein! *) 60let rec bereinige_winkel w = if w<0.0 61 then bereinige_winkel (w+.2.0*.pi) 62 else if w>=2.0*.pi 63 then bereinige_winkel (w-.2.0*.pi) 64 else w 65let kreispunkt (x,y) r w = x+.r*.(cos w), y+.r*.(sin w) 66 67let skaliere_punkt t (x,y) = t*.x, t*.y 68let addiere_punkte (x,y) (x',y') = x+.x', y+.y' 69let verschiebe_punkt dx dy = addiere_punkte (dx,dy) 70let drehe_punkt winkel (x,y) = 71 let c = cos winkel in 72 let s = sin winkel in 73 (c*.x-.s*.y, s*.x+.c*.y) 74let spiegel_punkt (x,y) = -.x, y 75 76let spline p1 p1' p2' p2 t = 77 let u = 1.0-.t in 78 addiere_punkte 79 (addiere_punkte 80 (skaliere_punkt (u*.u*.u) p1) 81 (skaliere_punkt (t*.t*.t) p2)) 82 (skaliere_punkt (3.0*.t*.u) 83 (addiere_punkte 84 (skaliere_punkt u p1') 85 (skaliere_punkt t p2'))) 86 87let spline_polynom_ableitung x1 x1' x2' x2 t = 88 3.0*.(x1'-.x1+.t*.(2.0*.(x2'-.2.0*.x1'+.x1)+.t*.(x2-.3.0*.(x2'-.x1')-.x1))) 89 90let spline_ableitung (x1,y1) (x1',y1') (x2',y2') (x2,y2) t = 91 spline_polynom_ableitung x1 x1' x2' x2 t, 92 spline_polynom_ableitung y1 y1' y2' y2 t 93 94type topologischer_ort = | Aussen | Innen | Rand 95 96let winkel_interval w1 w2 w vorwaerts = 97 let w1,w2,w = 98 bereinige_winkel w1, bereinige_winkel w2, bereinige_winkel w in 99 if w=w1 || w=w2 100 then Rand 101 else if w1=w2 102 then Aussen 103 else if xor (xor (w<w1) (w<w2)) (xor (w1<w2) vorwaerts) 104 then Innen 105 else Aussen 106 107 108 109 110(* 111 Der wichtige Typ f�rs Interface 112*) 113 114type linie = 115 | Strecke of (punkt * punkt) 116 | Kreis of (punkt * float) 117 | Bogen of (punkt * float * bool * float * float) 118 | Spline of (punkt * punkt * punkt * punkt) 119 120let linie_rueckwaerts = function 121 | Strecke (p1,p2) -> Strecke (p2,p1) 122 | Kreis k -> Kreis k 123 | Bogen (p,r,g,w1,w2) -> Bogen (p,r,not g,w2,w1) 124 | Spline (p1,p1',p2',p2) -> Spline (p2,p2',p1',p1) 125 126let verschiebe_linie dx dy = function 127 | Strecke (p1,p2) -> Strecke 128 (verschiebe_punkt dx dy p1, verschiebe_punkt dx dy p2) 129 | Kreis (p,r) -> Kreis (verschiebe_punkt dx dy p, r) 130 | Bogen (p,r,g,w1,w2) -> Bogen 131 (verschiebe_punkt dx dy p, r,g,w1,w2) 132 | Spline (p1,p1',p2',p2) -> Spline 133 (verschiebe_punkt dx dy p1, verschiebe_punkt dx dy p1', 134 verschiebe_punkt dx dy p2', verschiebe_punkt dx dy p2) 135 136let drehe_linie winkel = function 137 | Strecke (p1,p2) -> Strecke (drehe_punkt winkel p1, drehe_punkt winkel p2) 138 | Kreis (p,r) -> Kreis (drehe_punkt winkel p, r) 139 | Bogen (p,r,g,w1,w2) -> Bogen 140 (drehe_punkt winkel p, r, g, w1+.winkel, w2+.winkel) 141 | Spline (p1,p1',p2',p2) -> Spline 142 (drehe_punkt winkel p1, drehe_punkt winkel p1', 143 drehe_punkt winkel p2', drehe_punkt winkel p2) 144 145let skaliere_linie faktor = function 146 | Strecke (p1,p2) -> 147 Strecke (skaliere_punkt faktor p1, skaliere_punkt faktor p2) 148 | Kreis (p,r) -> Kreis (skaliere_punkt faktor p, r*.faktor) 149 | Bogen (p,r,g,w1,w2) -> 150 Bogen (skaliere_punkt faktor p, r*.faktor, g, w1, w2) 151 | Spline (p1,p1',p2',p2) -> Spline 152 (skaliere_punkt faktor p1, skaliere_punkt faktor p1', 153 skaliere_punkt faktor p2', skaliere_punkt faktor p2) 154 155let spiegel_linie = function 156 | Strecke (p1,p2) -> Strecke (spiegel_punkt p1, spiegel_punkt p2) 157 | Kreis (p,r) -> Kreis (spiegel_punkt p,r) 158 | Bogen (p,r,g,w1,w2) -> Bogen (spiegel_punkt p,r, not g, pi-.w1, pi-.w2) 159 | Spline (p1,p1',p2',p2) -> Spline 160 (spiegel_punkt p1, spiegel_punkt p1', spiegel_punkt p2', spiegel_punkt p2) 161 162let abstand_linie linie p = 163 (* Spline ist nicht erlaubt! *) 164 match linie with 165 | Strecke (p1,p2) -> 166 if p1=p2 167 then abstand p p1 168 else 169 let x1,y1 = p1 in 170 let x2,y2 = p2 in 171 let x0,y0 = p in 172 let skalar = skalar p1 in 173 let radial = (skalar p2 p)/.(skalar p2 p2) in 174 if radial < 0.0 175 then abstand p p1 176 else if radial > 1.0 177 then abstand p p2 178 else 179 let dx = x0-.x1-.radial*.(x2-.x1) in 180 let dy = y0-.y1-.radial*.(y2-.y1) in 181 sqrt (dx*.dx +. dy*.dy) 182 | Kreis (p0,r) -> abs_float (r-.(abstand p p0)) 183 | Bogen (p0,r,g,w1,w2) -> 184 if p=p0 185 then r 186 else if (winkel_interval w1 w2 (richtung p0 p) g)=Innen 187 then abs_float (r-.(abstand p p0)) 188 else min 189 (abstand p (kreispunkt p0 r w1)) 190 (abstand p (kreispunkt p0 r w2)) 191 192exception Endpunkt 193 194let schneidet_strecke p1 p2 p3 p4 = 195 (* Voraussetzung: p1 verschieden von p2 *) 196 if p3=p4 197 then false 198 else 199 let (x1,y1),(x2,y2),(x3,y3),(x4,y4) = p1,p2,p3,p4 in 200 (* 201 Wir l�sen das System t*p1+(1-t)*p2 = u*p3+(1-u)*p4, 202 also t*(p1-p2)+u*(p4-p3) = p4-p2 203 *) 204 let a11,a12,a21,a22 = x1-.x2, x4-.x3, y1-.y2, y4-.y3 in 205 let det = a11*.a22 -. a12*.a21 in 206 if det=0.0 207 then if a11*.(y3-.y1) = a21*.(x3-.x1) 208 (* Die Strecken sind parallel, aber sind sie auch kolinear? *) 209 then 210 (* Jetzt kommt es auf die Anordnung an *) 211 let z1,z2,z3,z4 = 212 if x1=x2 then y1,y2,y3,y4 else x1,x2,x3,x4 in 213 let z5,z6 = min z1 z2, max z1 z2 in 214 if (z3<z5 && z4<z5) || (z3>z6 && z4>z6) 215 (* Sind die Strecken schnittfrei? *) 216 then false 217 else raise Endpunkt 218 else false 219 else 220 let b1,b2 = x4-.x2, y4-.y2 in 221 let t,u = (b1*.a22 -. b2*.a12)/.det, (b2*.a11 -. b1*.a21)/.det in 222 if 0.0<=t && t<=1.0 && 0.0<=u && u<=1.0 223 then if (t > -.eps && t<eps) || (t>1.0-.eps && t<1.0+.eps) 224 || (u > -.eps && u<eps) || (u>1.0-.eps && u<1.0+.eps) 225 then raise Endpunkt 226 else true 227 else false 228 229let schnitte_spline p1 p2 p3 p3' p4' p4 = 230 (* Z�hlt die Schnitte des Splines (p3,p3',p4',p4) mit der Strecke (p1,p2). 231 Die Anzahl ber�cksichtigt Vielfachheiten. 232 Voraussetzung: p1 verschieden von p2 *) 233 (* Wir wollen l�sen: 234 (1-t)^3p3 + 3t(1-t)^2p3' + 3t^2(1-t)p4' + t^3p4 = (1-u)p1 + up2 235 Das ist: 236 t^3(p4-3p4'+3p3'-p3) + 3t^2(p4'-2p3'+p3) + 3t(p3'-p3) + p3 = u(p2-p1) + p1 237 *) 238 let (x1,y1),(x2,y2),(x3,y3),(x3',y3'),(x4',y4'),(x4,y4) 239 = p1,p2,p3,p3',p4',p4 in 240 let dx,dy = x2-.x1, y2-.y1 in 241 (* Wir wollen durch dx teilen d�rfen, 242 also notfalls alles an der Diagonale spiegeln. *) 243 let x1,y1,x2,y2,x3,y3,x3',y3',x4',y4',x4,y4,dx,dy = 244 if abs_float dx > abs_float dy 245 then x1,y1,x2,y2,x3,y3,x3',y3',x4',y4',x4,y4,dx,dy 246 else y1,x1,y2,x2,y3,x3,y3',x3',y4',x4',y4,x4,dy,dx in 247 (* Wir haben schon d=p2-p1. Mit d3=p3'-p3 und d'=p4'-p3' wollen wir l�sen: 248 t^3(p4-3d'-p3) + 3t^2(d'-d3) + 3td3 + p3 = ud + p1 *) 249 let dx3,dy3,dx',dy' = x3'-.x3, y3'-.y3, x4'-.x3', y4'-.y3' in 250 let uf = dy/.dx in 251 let c3x,c2x,c1x,c0x = x4-.3.0*.dx'-.x3, 3.0*.(dx'-.dx3), 3.0*.dx3, x3-.x1 in 252 (* Es folgt u=(c3xt^3 + c2xt^2 + c1xt + c0x)/dx. 253 Also bleibt zu l�sen: 254 t^3(y4-3dy'-y3 - ufc3x) 255 + t^2(3(dy'-dy3) - ufc2x) 256 + t(3dy3 - ufc1x) 257 + y3-y1 - ufc0x 258 = 0 *) 259 try 260 let ts = Polynome.loese_3 261 (y4-.3.0*.dy'-.y3-.uf*.c3x) 262 (3.0*.(dy'-.dy3)-.uf*.c2x) 263 (3.0*.dy3-.uf*.c1x) 264 (y3-.y1-.uf*.c0x) in 265 List.fold_left (fun anzahl -> fun t -> 266 if t<0.0 || t>1.0 267 then anzahl 268 else 269 let u = (c0x+.t*.(c1x+.t*.(c2x+.t*.c3x)))/.dx in 270 if u<0.0 || u>1.0 271 then anzahl 272 else if t=0.0 || t=1.0 || u=0.0 || u=1.0 273 then raise Endpunkt 274 else anzahl+1) 275 0 ts 276 with 277 | Polynome.Nullpolynom -> 278 (* Alle sechs Punkte kolinear. Wir k�nnen auf die x-Achse projizieren. 279 Als n�chstes brauchen wir die Ausdehnung des Splines. *) 280 let xmin,xmax = try 281 let extremstellen = Polynome.loese_2 282 (x4-.3.0*.dx'-.x3) 283 (2.0*.(dx'-.dx3)) 284 dx3 in 285 let randwerte = List.map 286 (fun t -> x3+.t*.(c1x+.t*.(c2x+.t*.c3x))) 287 (0.0::1.0::List.filter (fun t -> t>0.0 && t<1.0) extremstellen) in 288 list_min id randwerte, 289 list_max id randwerte 290 with 291 | Polynome.Nullpolynom -> (* Der Spline ist ein Punkt. *) 292 x3,x3 in 293 if (x1<xmin && x2<xmin) || (x1>xmax && x2>xmax) 294 then 0 295 else raise Endpunkt 296 297 298 299let schneidet_linie linie p1 p2 = if p1=p2 300 (* 301 Gibt es eine ungerade Anzahl an Schnittpunkten der Linie linie 302 mit der Strecke (p1,p2)? 303 Sonderf�lle: 304 Wenn die Linie oder die Strecke zu einem Punkt entartet ist, 305 darf die Antwort immer auch false sein. 306 Wenn sonst einer der Schnittpunkte ein Endpunkt der Linie oder 307 der Strecke ist, mu� Endpunkt geraist werden. 308 *) 309 then false 310 else match linie with 311 | Strecke (p3,p4) -> schneidet_strecke p1 p2 p3 p4 312 | Kreis (p,r) -> 313 let r1,r2 = abstand p p1, abstand p p2 in 314 if r1=r || r2=r 315 then raise Endpunkt 316 else xor (r1<r) (r2<r) 317 | Bogen (p,r,g,w1,w2) -> if r=0.0 318 then false 319 else 320 let (x1,y1),(x2,y2),(x,y) = p1,p2,p in 321 (* 322 Zuerst berechnen wir die Schnittpunkte mit dem Kreis: 323 d^2(t*p1+(1-t)*p2, p) = r^2 324 *) 325 let dx,dy,dx',dy' = x1-.x2, y1-.y2, x2-.x, y2-.y in 326 let a,b,c = 327 dx*.dx+.dy*.dy, dx*.dx'+.dy*.dy', dx'*.dx'+.dy'*.dy'-.r*.r in 328 let a',b' = b/.a, c/.a in 329 let disk = a'*.a'-.b' in 330 if disk < 0.0 331 then false (* Wir schneiden nicht mal den Kreis! *) 332 else 333 let t1,t2 = let wurzel = sqrt disk in -.wurzel-.a', wurzel-.a' in 334 let schneidet t = 335 if t>1.0 || t<0.0 336 then false 337 else 338 let u=1.0-.t in 339 match winkel_interval w1 w2 340 (richtung (t*.x1+.u*.x2, t*.y1+.u*.y2) p) g with 341 | Rand -> raise Endpunkt 342 | Innen -> if t=1.0 || t=0.0 343 then raise Endpunkt 344 else true 345 | Aussen -> false in 346 xor (schneidet t1) (schneidet t2) 347 | Spline (p3,p3',p4',p4) -> (schnitte_spline p1 p2 p3 p3' p4' p4) land 1=1 348 349exception Drauf 350 351let windung_strecke' w = 352 if w>=pi 353 then if w=pi 354 then raise Drauf 355 else w-.2.0*.pi 356 else if w <= -.pi 357 then if w = -.pi 358 then raise Drauf 359 else w+.2.0*.pi 360 else w 361 362let windung_strecke p p1 p2 = 363 (* Voraussetzung: p verschieden von p1,p2 *) 364 windung_strecke' ((richtung p p2)-.(richtung p p1)) 365 366let windung_spline p p1 p1' p2' p2 p3 = 367 (* p3 ist so, da� p nicht auf dem Zug p1p3p2 liegt, au�er p=p1 oder p=p2. *) 368 let zugwindung = windung_strecke p p1 p3 +. windung_strecke p p3 p2 in 369 let schnitte p' = 370 (* Anzahl der Schnitte der Geraden p'p mit dem Rundweg Spline+p2p3p1. *) 371 schnitte_spline p p' p1 p1' p2' p2 372 + (if schneidet_strecke p p' p2 p3 then 1 else 0) 373 + (if schneidet_strecke p p' p3 p1 then 1 else 0) in 374 let x0,y0 = List.fold_left 375 (fun (x,y) -> fun (x',y') -> min x x', min y y') 376 p [p1;p1';p2';p2;p3] in 377 let schnitte = 378 (* Wir probieren bei exception Endpunkt mehrere p' aus, bis wir sicher sind, 379 da� einmal davon der Endpunkt p ist und nicht etwa p1, p2 oder p3. *) 380 try schnitte (x0-.1.0,y0-.1.0) 381 with Endpunkt -> 382 try schnitte (x0-.2.0,y0-.1.0) 383 with Endpunkt -> 384 try schnitte (x0-.3.0,y0-.1.0) 385 with Endpunkt -> 386 try schnitte (x0-.4.0,y0-.1.0) 387 with Endpunkt -> raise Drauf in 388 if schnitte land 1 = 0 389 then zugwindung 390 else zugwindung+.2.0*.pi 391 392 393let windung_linie linie p = 394 match linie with 395 | Strecke (p1,p2) -> 396 if p=p1 || p=p2 397 then raise Drauf 398 else windung_strecke p p1 p2 399 | Kreis (p0,r) -> 400 let d=abstand p p0 in 401 if d<r then 2.0*.pi else if d>r then 0.0 else raise Drauf 402 | Bogen (p0,r,g,w1,w2) -> 403 let w1,w2 = if g then w1,w2 else w2,w1 in 404 let d=abstand p p0 in 405 if d=r 406 then if p=p0 407 then raise Drauf 408 else if (winkel_interval w1 w2 (richtung p0 p) true) = Innen 409 then raise Drauf 410 else () 411 else (); 412 let w= 413 (richtung p (kreispunkt p0 r w2)) -. 414 (richtung p (kreispunkt p0 r w1)) in 415 let w = if d>r 416 then windung_strecke' w 417 else bereinige_winkel w in 418 if g then w else -.w 419 | Spline (p1,p1',p2',p2) -> 420 if p=p1 || p=p2 421 then raise Drauf 422 else 423 let (x,y),(x1,y1),(x2,y2) = p,p1,p2 in 424 let dx,dy = x2-.x1, y2-.y1 in 425 let xm,ym = (x1+.x2)/.2.0, (y1+.y2)/.2.0 in 426 let p3a = xm+.dy, ym-.dx in 427 let p3b = xm-.dy, ym+.dx in 428 let p3 = if abstand p p3a > abstand p p3b then p3a else p3b in 429 windung_spline p p1 p1' p2' p2 p3 430 431 432 433let windung_drin windungszahl = 434 let w = truncate (floor (windungszahl/.(2.0*.pi)+.0.5)) in 435 (w land 1)<>0 436 437 438 439type polygon = linie list 440 441let verschiebe_polygon dx dy = List.map (verschiebe_linie dx dy) 442let drehe_polygon w = List.map (drehe_linie (w*.pi/.180.0)) 443let skaliere_polygon f = List.map (skaliere_linie f) 444let spiegel_polygon = List.map spiegel_linie 445 446 447 448module type Malmethode = sig 449 450 type polygon' 451 452 val konvertiere_polygon : polygon -> polygon' 453 val punkt_auf_polygon_relativ : polygon' -> float -> punkt * float option 454 val rueckwaerts: polygon' -> polygon' 455 456 type vektording = 457 | Strich of (farbe * polygon' list) 458 | Dicker_Strich of (farbe * float * polygon' list) 459 | Flaechen of (farbe array * (polygon' * int * int option) list) 460 461 val flaeche : farbe -> polygon' list -> vektording 462 val pixel_zu_dingen : farbe option -> pixelbild -> vektording list 463 464 val map_vektordinge: (linie -> linie) -> vektording list -> vektording list 465 val verschiebe_dinge: float -> float -> vektording list -> vektording list 466 val drehe_dinge: float -> vektording list -> vektording list 467 val skaliere_dinge: float -> vektording list -> vektording list 468 val spiegel_dinge: vektording list -> vektording list 469 470 type vektorbild 471 472 val erzeuge_vektorbild : vektording list -> vektorbild 473 val male: vektorbild -> float -> bildchen -> bildchen 474 475end 476 477 478 479type laengen_baum = 480 | Blatt of float 481 | Nichtblatt of (laengen_baum * float * laengen_baum * float) 482 (* erster float: Kurvenparameter in der Mitte 483 zweiter float: Gesamtl�nge *) 484 485let erstelle_laengen_baum f tmin tmax = 486 let rec teile t1 p1 t2 p2 l = 487 let t3 = (t1+.t2)*.0.5 in 488 let p3 = f t3 in 489 let l1,l2 = abstand p1 p3, abstand p3 p2 in 490 let (b1,l1'),(b2,l2') = if (l1+.l2)/.l < laengen_fehler 491 then (Blatt l1, l1), (Blatt l2, l2) 492 else teile t1 p1 t3 p3 l1, teile t3 p3 t2 p2 l2 in 493 let l' = l1'+.l2' in 494 Nichtblatt (b1,t3,b2,l'), l' in 495 let pmin,pmax = f tmin, f tmax in 496 fst (teile tmin pmin tmax pmax (abstand pmin pmax)) 497 498let erstelle_laengen_baum l = match l with 499 | Strecke (p1,p2) -> Blatt (abstand p1 p2) 500 | Bogen (p,r,g,w1,w2) -> Blatt (r*.(abs_float (w1-.w2))) 501 | Spline (p1,p1',p2',p2) -> 502 erstelle_laengen_baum (spline p1 p1' p2' p2) 0.0 1.0 503 504let laenge b = match b with 505 | Blatt l -> l 506 | Nichtblatt (b1,t,b2,l) -> l 507 508let rec suche_laenge_absolut l0 t1 t2 b = match b with 509 | Blatt l -> t1+.(t2-.t1)*.l0/.l 510 | Nichtblatt (b1,t3,b2,l3) -> 511 let l1 = laenge b1 in 512 if l0<=l1 513 then suche_laenge_absolut l0 t1 t3 b1 514 else suche_laenge_absolut (l0-.l1) t3 t2 b2 515 516 517 518module Methode_Daten_abstrakt = struct 519 520 type polygon' = (linie * laengen_baum lazyval ref) list * bool 521 (* Der bool steht f�r r�ckw�rts *) 522 523 let rueckwaerts (p,r) = p, not r 524 525 let erzeuge_laenge l = l, set_lazy (function u -> erstelle_laengen_baum l) 526 527 let konvertiere_polygon pol = List.map erzeuge_laenge pol, false 528 529 let punkt_auf_polygon_relativ (p,r) t = 530 let l = list_sum (function l,b -> laenge (get_lazy b)) p in 531 let l0 = if r then (1.0-.t)*.l else t*.l in 532 let rec suche l ((li,b)::t) = 533 let b' = get_lazy b in 534 let lh = laenge b' in 535 if l<=lh 536 then li, suche_laenge_absolut l 0.0 1.0 b' 537 else suche (l-.l0) t in 538 let li,t = suche l0 p in 539 let p,w = match li with 540 | Strecke (p1,p2) -> 541 addiere_punkte (skaliere_punkt (1.0-.t) p1) (skaliere_punkt t p2), 542 if p1=p2 then None else Some (richtung p1 p2) 543 | Bogen (p,r,g,w1,w2) -> 544 let w = w1+.t*.(w2-.w1) in 545 kreispunkt p r w, 546 Some (w+.pi*.(if g then 0.5 else -0.5)) 547 | Spline (p1,p1',p2',p2) -> 548 spline p1 p1' p2' p2 t, 549 let dx,dy = spline_ableitung p1 p1' p2' p2 t in 550 if dx=0.0 && dy=0.0 then None else Some (atan2 dy dx) in 551 match w with 552 | None -> p,None 553 | Some w -> p, Some ((if r then w+.pi else w)*.180.0/.pi) 554 555 556 557 type polygon'' = linie list * bool 558 559 let konvertiere_polygon' (p,r) = List.map fst p, r 560 561 let map_polygon' f (p,r) = 562 List.map 563 (function l,b -> erzeuge_laenge (f l)) 564 p, 565 r 566 567 568 569 type vektording = 570 | Strich of (farbe * polygon' list) 571 | Dicker_Strich of (farbe * float * polygon' list) 572 | Flaechen of (farbe array * (polygon' * int * int option) list) 573 574 let flaeche f ps = Flaechen ([|f|], List.map (function p -> p,0,None) ps) 575 576 type pzd_pos = 577 | PZD_Punkt 578 | PZD_Innen 579 | PZD_Aussen 580 581 let pixel_zu_dingen auslass bild = 582 let breite,hoehe,pixel = bild in 583 if breite=0 || hoehe=0 584 then [] 585 else 586 let palette,farbindex = extrahiere_farben bild in 587 (* Ab jetzt sind Farben immer nur Indices in die palette. *) 588 let auslass = match auslass with 589 | None -> None 590 | Some f -> 591 let i = FarbMap.find f farbindex in 592 if f=palette.(i) then Some i else None in 593 let pos_zu_punkt (x,y) = float_of_int x, float_of_int (hoehe-y) in 594 let pos_zu_farbe (x,y) = if x>=0 && x<breite && y>=0 && y<hoehe 595 then Some (FarbMap.find pixel.(y).(x) farbindex) 596 else None in 597 let rand = Array.make (Array.length palette) [] in 598 let binnen = Array.init (Array.length palette) 599 (fun i -> Array.make i []) in 600 let reihe pos = 601 let rec doit i start innen aussen = 602 (* pos : int -> pzd -> (int*int) wandelt i in eine Position um. 603 i : int ist der Schleifenindex. 604 start : punkt ist der Startpunkt der Kante, deren Ende gerade 605 gesucht wird. 606 innen : farbe option und aussen : farbe option sind die Farben, 607 die diese Kante trennt trennt. 608 *) 609 let innen' = pos_zu_farbe (pos i PZD_Innen) in 610 let aussen' = pos_zu_farbe (pos i PZD_Aussen) in 611 if (innen',aussen')=(innen,aussen) 612 then doit (i+1) start innen aussen 613 else 614 (let ende = pos_zu_punkt (pos i PZD_Punkt) in 615 let innen = if innen=auslass then None else innen in 616 let aussen = if aussen=auslass then None else aussen in 617 (match innen,aussen with 618 | (Some f1, Some f2) -> if f1=f2 619 then () 620 else if f1<f2 621 then binnen.(f2).(f1) <- 622 (Strecke (start,ende))::binnen.(f2).(f1) 623 else binnen.(f1).(f2) <- 624 (Strecke (ende,start))::binnen.(f1).(f2) 625 | (Some f, None) -> rand.(f) <- (Strecke (ende,start))::rand.(f) 626 | (None, Some f) -> rand.(f) <- (Strecke (start,ende))::rand.(f) 627 | (None,None) -> ()); 628 if (innen',aussen') <> (None,None) 629 then doit (i+1) ende innen' aussen') in 630 doit 1 (pos_zu_punkt (pos 0 PZD_Punkt)) 631 (pos_zu_farbe (pos 0 PZD_Innen)) 632 (pos_zu_farbe (pos 0 PZD_Aussen)) in 633 for j = 0 to hoehe do 634 reihe (fun i -> fun pzd -> (i, j - if pzd=PZD_Aussen then 1 else 0)) 635 done; 636 for j = 0 to breite do 637 reihe (fun i -> fun pzd -> ((j - if pzd=PZD_Innen then 1 else 0), i)) 638 done; 639 let einfach_grenzen = array_foldi 640 (fun grenzen -> fun farbe -> fun grenze -> if grenze=[] 641 then grenzen 642 else (konvertiere_polygon grenze,farbe,None)::grenzen) 643 [] rand in 644 let grenzen = array_foldi 645 (fun grenzen -> fun farbe1 -> array_foldi 646 (fun grenzen -> fun farbe2 -> fun grenze -> if grenze=[] 647 then grenzen 648 else (konvertiere_polygon grenze,farbe1,Some farbe2)::grenzen) 649 grenzen) 650 einfach_grenzen 651 binnen in 652 [Flaechen (palette,grenzen)] 653 654 655 656 type vektording' = 657 | Strich' of (farbe * polygon'' list) 658 | Dicker_Strich' of (farbe * float * polygon'' list) 659 | Flaechen' of (farbe array * (polygon'' * int * int option) list) 660 661 type vektorbild = vektording' list 662 663 let erzeuge_vektorbild vektordinge = List.concat (List.map 664 (function 665 | Strich (f,ps) -> 666 let ps' = List.map konvertiere_polygon' 667 (List.filter (function p,r -> p<>[]) ps) in 668 if ps'=[] then [] else [Strich' (f,ps')] 669 | Dicker_Strich (f,d,ps) -> 670 let ps' = List.map konvertiere_polygon' 671 (List.filter (function p,r -> p<>[]) ps) in 672 if ps'=[] then [] else [Dicker_Strich' (f,d,ps')] 673 | Flaechen (fs,ps) -> [Flaechen' 674 (fs, List.map (function p,f1,f2 -> konvertiere_polygon' p,f1,f2) 675 (List.filter (function (p,r),f1,f2 -> p<>[]) ps))]) 676 vektordinge) 677 678 let map_vektordinge f_ = List.map 679 (function 680 | Strich (f,ps) -> Strich (f, List.map (map_polygon' f_) ps) 681 | Dicker_Strich (f,d,ps) -> 682 Dicker_Strich (f, d, List.map (map_polygon' f_) ps) 683 | Flaechen (fs,ps) -> Flaechen 684 (fs, List.map (function p,f1,f2 -> map_polygon' f_ p,f1,f2) ps)) 685 686 let verschiebe_dinge dx dy = map_vektordinge (verschiebe_linie dx dy) 687 let drehe_dinge winkel = map_vektordinge (drehe_linie (winkel*.pi/.180.0)) 688 let skaliere_dinge faktor = map_vektordinge (skaliere_linie faktor) 689 let spiegel_dinge = map_vektordinge spiegel_linie 690 691end 692 693 694 695type rahmen = 696 | Linie of linie 697 | Teilung of 698 (float * float * float * float * punkt * punkt * rahmen * rahmen) 699 (* xmin, ymin, xmax, ymax, p1, p2, Linie p1-p3, Linie p3-p2 *) 700 701let rahmen_rand r = match r with 702 | Linie (Strecke (p1,p2)) -> 703 let (x1,y1),(x2,y2) = p1,p2 in 704 min x1 x2, min y1 y2, max x1 x2, max y1 y2, p1, p2 705 | Linie (Kreis ((x,y),r)) -> 706 x-.r, y-.r, x+.r, y+.r, (0.0,0.0), (0.0,0.0) 707 | Linie (Bogen (p,r,g,w1,w2)) -> 708 let p1,p2 = kreispunkt p r w1, kreispunkt p r w2 in 709 let (x,y),(x1,y1),(x2,y2) = p,p1,p2 in 710 let xe = x1::x2::(List.concat (List.map 711 (function x,w -> if winkel_interval w1 w2 w g = Innen then [x] else []) 712 [x+.r, 0.0; x-.r, pi])) in 713 let ys = y1::y2::(List.concat (List.map 714 (function y,w -> if winkel_interval w1 w2 w g = Innen then [y] else []) 715 [y+.r, pi*.0.5; y-.r, pi*.1.5])) in 716 list_min (function x -> x) xe, list_min (function y -> y) ys, 717 list_max (function x -> x) xe, list_max (function y -> y) ys, 718 p1, p2 719 | Linie (Spline (p1,p1',p2',p2)) -> 720 let (x1,y1),(x1',y1'),(x2',y2'),(x2,y2) = p1,p1',p2',p2 in 721 let x_extremstellen = 722 let a = x2-.3.0*.(x2'-.x1')-.x1 in 723 let b = x2'-.2.0*.x1'+.x1 in 724 let c = x1'-.x1 in 725 List.map 726 (function t -> x1+.t*.(3.0*.c+.t*.(3.0*.b+.t*.a))) 727 (0.0::1.0::(List.filter 728 (function t -> 0.0<t && t<1.0) 729 (Polynome.loese_2 a (2.0*.b) c))) in 730 let y_extremstellen = 731 let a = y2-.3.0*.(y2'-.y1')-.y1 in 732 let b = y2'-.2.0*.y1'+.y1 in 733 let c = y1'-.y1 in 734 List.map 735 (function t -> y1+.t*.(3.0*.c+.t*.(3.0*.b+.t*.a))) 736 (0.0::1.0::(List.filter 737 (function t -> 0.0<t && t<1.0) 738 (Polynome.loese_2 a (2.0*.b) c))) in 739 list_min (function x -> x) x_extremstellen, 740 list_min (function y -> y) y_extremstellen, 741 list_max (function x -> x) x_extremstellen, 742 list_max (function y -> y) y_extremstellen, 743 p1, p2 744 | Teilung (x1,y1,x2,y2,p1,p2,r13,r32) -> x1,y1,x2,y2,p1,p2 745 746let kombiniere_rahmen r1 r2 = 747 let x1,y1,x2,y2,p1,p3 = rahmen_rand r1 in 748 let x1',y1',x2',y2',p3,p2 = rahmen_rand r2 in 749 Teilung (min x1 x1', min y1 y1', max x2 x2', max y2 y2', p1, p2, r1, r2) 750 751let unterteile nahe f t1 t2 = 752 (* Ersetzt die Kurve f zwischen t1 und t2 durch einen Rahmen, in dem 753 ein Streckenzug steckt. 754 nahe gibt an, wann nicht weiter unterteilt werden mu�. *) 755 let rec intern t ft t' ft' = 756 if nahe ft ft' 757 then Linie (Strecke (ft,ft')) 758 else 759 let t'' = (t+.t')/.2.0 in 760 let ft'' = f t'' in 761 kombiniere_rahmen (intern t ft t'' ft'') (intern t'' ft'' t' ft') in 762 intern t1 (f t1) t2 (f t2) 763 764 765let unterteile_kontinuierlich maxabstand = unterteile 766 (function p1 -> function p2 -> abstand p1 p2 <= maxabstand) 767 768let unterteile_raster pixelkantenlaenge f = 769 let runde x = ((floor (x/.pixelkantenlaenge))+.0.5)*.pixelkantenlaenge in 770 unterteile 771 (function p1 -> function p2 -> abstand p1 p2 < 1.5*.pixelkantenlaenge) 772 (function t -> let x,y = f t in runde x, runde y) 773 774 775 776module type Unterteile_Methode = sig 777 778 val unterteile: float -> polygon -> rahmen list * rahmen list 779 (* erstes Ergebnis: F�r Abstand zweites: f�r Windung *) 780 781end 782 783module Unterteile_kontinuierlich = struct 784 785 let unterteile pixelkantenlaenge pol = 786 List.map 787 (function 788 | Strecke s -> Linie (Strecke s) 789 | Kreis k -> Linie (Kreis k) 790 | Bogen b -> Linie (Bogen b) 791 | Spline (p1,p1',p2',p2) -> unterteile_kontinuierlich 792 pixelkantenlaenge 793 (spline p1 p1' p2' p2) 794 0.0 1.0) 795 pol, 796 List.map (function l -> Linie l) pol 797 798end 799 800module Unterteile_Raster = struct 801 802 let unterteile_linie_raster pixelkantenlaenge linie = 803 let unterteile = unterteile_raster pixelkantenlaenge in 804 match linie with 805 | Strecke (p1,p2) -> unterteile (function t -> addiere_punkte 806 (skaliere_punkt (1.0-.t) p1) (skaliere_punkt t p2)) 0.0 1.0 807 | Kreis (p,r) -> kombiniere_rahmen 808 (unterteile (kreispunkt p r) 0.0 pi) 809 (unterteile (kreispunkt p r) pi (pi+.pi)) 810 | Bogen (p,r,g,w1,w2) -> 811 let w1,w2 = bereinige_winkel w1, bereinige_winkel w2 in 812 let w1,w2 = if g 813 then if w1<=w2 then w1,w2 else w1, w2+.pi+.pi 814 else if w1>=w2 then w1,w2 else w1+.pi+.pi, w2 in 815 unterteile (kreispunkt p r) w1 w2 816 | Spline (p1,p1',p2',p2) -> unterteile (spline p1 p1' p2' p2) 0.0 1.0 817 818 let unterteile pixelkantenlaenge pol = 819 let unterteilt = List.map 820 (unterteile_linie_raster pixelkantenlaenge) 821 pol in 822 unterteilt, unterteilt 823 824end 825 826 827 828module type Zeichne_Methode = sig 829 830 val mische: farbe lazyval ref -> farbe -> float -> farbe 831 (* float: Anteil der ersten Farbe *) 832 833end 834 835module Zeichne_kontinuierlich = struct 836 837 let mische farbe1 farbe2 anteil = if anteil>=1.0 838 then get_lazy farbe1 839 else if anteil<=0.0 840 then farbe2 841 else misch2 farbe2 (get_lazy farbe1) anteil 842 843end 844 845module Zeichne_diskret = struct 846 847 let mische farbe1 farbe2 anteil = if anteil>0.5 848 then get_lazy farbe1 849 else farbe2 850 851end 852 853 854 855let rec abstand_rahmen r p0 maxabstand = match r with 856 | Linie l -> abstand_linie l p0 857 | Teilung (x1,y1,x2,y2,p1,p2,r1,r2) -> 858 let x,y = p0 in 859 if (x<x1-.maxabstand) || (x>x2+.maxabstand) || 860 (y<y1-.maxabstand) || (y>y2+.maxabstand) 861 then maxabstand 862 else min 863 (abstand_rahmen r1 p0 maxabstand) 864 (abstand_rahmen r2 p0 maxabstand) 865 866let abstand p0 maxabstand = list_min 867 (function r -> abstand_rahmen r p0 maxabstand) 868 869let rec windung_rahmen r p0 = match r with 870 | Linie l -> windung_linie l p0 871 | Teilung (x1,y1,x2,y2,p1,p2,r1,r2) -> 872 let x,y = p0 in 873 if x<x1 || x>x2 || y<y1 || y>y2 874 then windung_strecke p0 p1 p2 875 else (windung_rahmen r1 p0)+.(windung_rahmen r2 p0) 876 877let windungszahl p0 = list_sum (function r -> windung_rahmen r p0) 878 879module Male = functor (U: Unterteile_Methode) -> 880 functor (Z: Zeichne_Methode) -> struct 881 882 include Methode_Daten_abstrakt 883 884 type vektording'' = 885 | Strich'' of (farbe * int list) 886 | Dicker_Strich'' of (farbe * float * int list) 887 | Flaechen'' of 888 (farbe * (int * bool) list * (int * bool * farbe) list) list 889 890 type vektorbild'' = 891 int * (rahmen list * rahmen list) array * vektording'' list 892 893 let konvertiere pixelkantenlaenge bild = 894 let extrahiere_polygon pol = 895 let rec suche i = function 896 | [] -> None 897 | h::t -> if h=pol then Some i else suche (i+1) t in 898 suche 0 in 899 let extrahiere_polygone pd = List.fold_left 900 (function (n,pols),exts -> function pol,r -> 901 match extrahiere_polygon pol pols with 902 | None -> (n+1,pols@[pol]),(n,r)::exts 903 | Some i -> (n,pols),(i,r)::exts) 904 (pd,[]) in 905 let ohne_richtung = List.map fst in 906 let (n,pols),bild' = List.fold_left 907 (function pd,dinge -> (function 908 | Strich' (f,ps) -> 909 let pd',ps' = extrahiere_polygone pd ps in 910 pd',dinge@[Strich'' (f,ohne_richtung ps')] 911 | Dicker_Strich' (f,d,ps) -> 912 let pd',ps' = extrahiere_polygone pd ps in 913 pd',dinge@[Dicker_Strich'' (f,d,ohne_richtung ps')] 914 | Flaechen' (fs,ps) -> 915 let pd',ps' = extrahiere_polygone pd 916 (List.map (function p,s,s' -> p) ps) in 917 let ps'' = List.map 918 (function (p,r),s,s' -> 919 let Some p' = extrahiere_polygon p (snd pd') in 920 (p',r),s,s') 921 ps in 922 pd',dinge@[Flaechen'' (Array.to_list (Array.mapi 923 (function i -> function f -> 924 let vollhauptkanten = List.map 925 (function (p,r),s,Some s' -> p,r,fs.(s')) 926 (List.filter (function p,s,s' -> s=i && s'<>None) ps'') in 927 let halbhauptkanten = List.map 928 (function (p,r),s,s' -> p,r) 929 (List.filter (function p,s,s' -> s=i && s'=None) ps'') in 930 let nebenkanten = List.map 931 (function (p,r),s,s' -> p,not r,fs.(s)) 932 (List.filter (function p,s,s' -> s'=Some i) ps'') in 933 let vollkanten = vollhauptkanten@nebenkanten in 934 f, 935 halbhauptkanten @ (List.map (function p,r,s -> p,r) vollkanten), 936 vollkanten) 937 fs))])) 938 ((0,[]),[]) 939 bild in 940 n, 941 Array.init n 942 (function i -> U.unterteile pixelkantenlaenge (List.nth pols i)), 943 bild' 944 945 let male bild aufloesung (breite,hoehe,farben) = 946 let halbaufloseung = aufloesung*.0.5 in 947 let npols,pols,bild = konvertiere aufloesung bild in 948 (breite,hoehe, 949 function p -> 950 let abstaende = Array.make npols (0.0, 0.0) in 951 let abstand maxabstand i = 952 let abstandi,maxabstandi = abstaende.(i) in 953 if maxabstandi>=maxabstand 954 then abstandi 955 else 956 let d = abstand p maxabstand (fst pols.(i)) in 957 (abstaende.(i) <- (d,maxabstand); d) in 958 let abstand' maxabstand = list_min (abstand maxabstand) in 959 let windungen = Array.make npols None in 960 let windung i = match windungen.(i) with 961 | None -> 962 let w = windungszahl p (snd pols.(i)) in 963 (windungen.(i) <- Some w; w) 964 | Some w -> w in 965 let windung_drin pols = windung_drin (list_sum 966 (function pol,r -> let w=windung pol in if r then -.w else w) 967 pols) in 968 get_lazy (List.fold_left 969 (function f -> (function 970 | Strich'' (farbe,pols) -> set_lazy (function u -> 971 Z.mische f farbe ((abstand' aufloesung pols)/.aufloesung)) 972 | Dicker_Strich'' (farbe,dicke,pols) -> set_lazy (function u -> 973 Z.mische f farbe 974 (((abstand' (dicke+.halbaufloseung) pols) 975 -.dicke)/.aufloesung+.0.5)) 976 | Flaechen'' flaechen -> set_lazy (function u -> 977 try 978 let farbe,kanten,vollkanten = List.find 979 (function farbe,kanten,vollkanten -> 980 try windung_drin kanten with | Drauf -> true) 981 flaechen in 982 let abstaende = 983 List.filter (function d,f' -> d<0.5) 984 (List.map (function pol,r,f' -> 985 (abstand aufloesung pol)/.aufloesung,f') 986 vollkanten) in 987 if abstaende = [] 988 then farbe 989 else misch 990 ((0.5+.(list_min fst abstaende), farbe):: 991 (List.map (function d,f -> 0.5-.d,f) abstaende)) 992 with 993 | Not_found -> get_lazy f))) 994 (set_lazy (function u -> farben p)) 995 bild)) 996 997end 998 999module Male_mit_aa = Male(Unterteile_kontinuierlich)(Zeichne_kontinuierlich) 1000module Male_ohne_aa = Male(Unterteile_Raster)(Zeichne_diskret) 1001 1002 1003