164 lines
		
	
	
		
			7.5 KiB
		
	
	
	
		
			HTML
		
	
	
	
	
	
			
		
		
	
	
			164 lines
		
	
	
		
			7.5 KiB
		
	
	
	
		
			HTML
		
	
	
	
	
	
<!DOCTYPE html>
 | 
						|
<html lang="en">
 | 
						|
    <head>
 | 
						|
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
 | 
						|
        <title>Doctests</title>
 | 
						|
        <link rel="stylesheet" href="./3rdparty/highlight/styles/default.css" />
 | 
						|
        <link rel="stylesheet" href="../css/doctest.css" />
 | 
						|
        <script src="./3rdparty/highlight/highlight.pack.js"></script>
 | 
						|
        <script src="../dist/iwmlib.js"></script>
 | 
						|
    </head>
 | 
						|
    <body onload="Doctest.run()">
 | 
						|
        <main>
 | 
						|
            <h1><a href="index.html">lib.</a>Utils</h1>
 | 
						|
            <p>
 | 
						|
                Utility functions can be used across modules. To avoid name conflicts most of them are defined as static
 | 
						|
                class functions (i.e. the class mainly serves as a namespace). Typically this class name is in the
 | 
						|
                plural, e.g. "Points", "Dates" to ensure that existing class names like "Point", "Date" are not in
 | 
						|
                conflict with the namespace.
 | 
						|
            </p>
 | 
						|
            <h2>Cycle</h2>
 | 
						|
            <p>Cycles simplify to switch between values in a cyclic way.</p>
 | 
						|
 | 
						|
            <script class="doctest">
 | 
						|
                let cycle = new Cycle(1, 2, 3)
 | 
						|
                Doctest.expect(cycle.next(), 1)
 | 
						|
                Doctest.expect(cycle.next(), 2)
 | 
						|
                Doctest.expect(cycle.next(), 3)
 | 
						|
                Doctest.expect(cycle.next(), 1)
 | 
						|
            </script>
 | 
						|
 | 
						|
            <h2>Dates</h2>
 | 
						|
            <script class="doctest">
 | 
						|
                let feb1900 = new Date(1900, 1, 1)
 | 
						|
                Doctest.expect(Dates.daysInMonth(feb1900), 28)
 | 
						|
                // 1900 was no leap year
 | 
						|
                let feb2000 = new Date(2000, 1, 1)
 | 
						|
                Doctest.expect(Dates.daysInMonth(feb2000), 29)
 | 
						|
                // 2000 was a leap year
 | 
						|
 | 
						|
                let mar1913 = new Date(1913, 2, 1)
 | 
						|
                Doctest.expect(Dates.daysInMonth(mar1913), 31)
 | 
						|
            </script>
 | 
						|
 | 
						|
            <p>
 | 
						|
                A tricky problem is to iterate over years, months, and days to label timelines and calendars in a
 | 
						|
                consistent way. This can lead to problems with standard (CET) and summer time (CEST). To illustrate the
 | 
						|
                problem look at the following example. Although march has 31 days the formatted UTC string shows "30.3".
 | 
						|
                Also note that the standard new Date() constructor uses a zero-based month:
 | 
						|
            </p>
 | 
						|
            <script class="doctest">
 | 
						|
                let format = { timeZone: 'UTC' }
 | 
						|
                let lastMar1913 = new Date(1913, 2, 31)
 | 
						|
                Doctest.expect(lastMar1913.toLocaleDateString('de', format), '30.3.1913')
 | 
						|
            </script>
 | 
						|
            <p>The following iterators guarantee that correct labels are generated:</p>
 | 
						|
 | 
						|
            <script class="doctest">
 | 
						|
                let lastDay = null
 | 
						|
                for (let day of Dates.iterDays(mar1913)) {
 | 
						|
                    lastDay = day
 | 
						|
                }
 | 
						|
                Doctest.expect(lastDay.toLocaleDateString('de', format), '31.3.1913')
 | 
						|
            </script>
 | 
						|
 | 
						|
            <h2>Sets</h2>
 | 
						|
            <p>
 | 
						|
                Unfortunately the common set operations of other languages are missing in JavaScript. Therefore we use a
 | 
						|
                Sets helper class with static methods:
 | 
						|
            </p>
 | 
						|
            <script class="doctest">
 | 
						|
                let set1 = new Set([1, 2, 3])
 | 
						|
                let set2 = new Set([2, 3, 4, 5])
 | 
						|
                let set3 = new Set([2, 3, 6])
 | 
						|
 | 
						|
                Doctest.expect(Array.from(Sets.intersect(set1, set2, set3)), [2, 3])
 | 
						|
                Doctest.expect(Array.from(Sets.union(set1, set2, set3)), [1, 2, 3, 4, 5, 6])
 | 
						|
                Doctest.expect(Array.from(Sets.difference(set2, set1, set3)), [4, 5])
 | 
						|
            </script>
 | 
						|
            <h2>Polygon</h2>
 | 
						|
            <p>
 | 
						|
                An intersection of polygons is needed to compute the overlap of rotated rectangles. We are using the
 | 
						|
                library <a href="https://gist.github.com/cwleonard/e124d63238bda7a3cbfa">jspolygon.js</a> but provide a
 | 
						|
                more convenient API that is compatible with arrays of absolute points.
 | 
						|
            </p>
 | 
						|
            <p>
 | 
						|
                To detect intersection with another Polygon object, the instance method uses the Separating Axis
 | 
						|
                Theorem. It returns false if there is no intersection, or an object if there is. The object contains 2
 | 
						|
                fields, overlap and axis. Moving the other polygon by overlap on axis will get the polygons out of
 | 
						|
                intersection.
 | 
						|
            </p>
 | 
						|
            <p>
 | 
						|
                The following triangles show an overlap. Moving the triangle along the red line would remove the
 | 
						|
                overlap.
 | 
						|
            </p>
 | 
						|
            <canvas id="canvas" class="grayBorder interactive">Canvas not supported</canvas>
 | 
						|
            <script class="doctest">
 | 
						|
                let context = canvas.getContext('2d')
 | 
						|
                // The jspolygon syntax
 | 
						|
                let a = Polygon.fromPoints([
 | 
						|
                    { x: 20, y: 20 },
 | 
						|
                    { x: 100, y: 100 },
 | 
						|
                    { x: 150, y: 50 }
 | 
						|
                ])
 | 
						|
                a.draw(context)
 | 
						|
                let b = Polygon.fromPoints([
 | 
						|
                    { x: 70, y: 50 },
 | 
						|
                    { x: 150, y: 10 },
 | 
						|
                    { x: 200, y: 70 }
 | 
						|
                ])
 | 
						|
                b.draw(context)
 | 
						|
 | 
						|
                context.strokeStyle = '#ff0000'
 | 
						|
                context.beginPath()
 | 
						|
                let result = a.intersectsWith(b)
 | 
						|
                if (result != false) {
 | 
						|
                    let { overlap, axis } = result
 | 
						|
                    context.moveTo(b.center.x, b.center.y)
 | 
						|
                    let target = Points.add(b.center, { x: overlap * axis.x, y: overlap * axis.y })
 | 
						|
                    context.lineTo(target.x, target.y)
 | 
						|
                }
 | 
						|
                context.stroke()
 | 
						|
            </script>
 | 
						|
            <h2>Low Pass Filter</h2>
 | 
						|
            <p>
 | 
						|
                Low Pass Filter muffles fast (high-frequency) changes to the signal. For more information visit the
 | 
						|
                <a href="http://en.wikipedia.org/wiki/Low-pass_filter">wikipedia article</a>.
 | 
						|
            </p>
 | 
						|
            <script class="doctest">
 | 
						|
                let lpf = new LowPassFilter(0.5)
 | 
						|
                Doctest.expect(
 | 
						|
                    lpf.smoothArray([10, 8, 9, 10, 12, 8, 50, 10, 12, 8]),
 | 
						|
                    [10, 9, 9, 10, 11, 9, 30, 20, 16, 12]
 | 
						|
                )
 | 
						|
 | 
						|
                Doctest.expect(lpf.next(20), 10.0)
 | 
						|
                Doctest.expect(lpf.next(20), 12.5)
 | 
						|
                Doctest.expect(lpf.next(20), 14.375)
 | 
						|
                Doctest.expect(lpf.next(20), 15.78125)
 | 
						|
 | 
						|
                lpf = new LowPassFilter(0.2)
 | 
						|
                lpf.setup([10, 10, 10, 10, 10, 10, 10, 10, 10, 10])
 | 
						|
                Doctest.expect(lpf.next(20), 12.0)
 | 
						|
                Doctest.expect(lpf.next(10), 10.32)
 | 
						|
            </script>
 | 
						|
            <h2>References</h2>
 | 
						|
            <ul>
 | 
						|
                <li><a href="https://en.wikipedia.org/wiki/Circular_buffer"> Circular buffer </a></li>
 | 
						|
                <li>
 | 
						|
                    <a href="http://stackoverflow.com/questions/20867562/create-a-date-object-with-cet-timezone">
 | 
						|
                        Create a Date object with CET timezone
 | 
						|
                    </a>
 | 
						|
                </li>
 | 
						|
                <li>
 | 
						|
                    <a
 | 
						|
                        href="http://stackoverflow.com/questions/315760/what-is-the-best-way-to-determine-the-number-of-days-in-a-month-with-javascript"
 | 
						|
                    >
 | 
						|
                        What is the best way to determine the number of days in a month with javascript?
 | 
						|
                    </a>
 | 
						|
                </li>
 | 
						|
            </ul>
 | 
						|
        </main>
 | 
						|
    </body>
 | 
						|
</html>
 |