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