153 lines
		
	
	
		
			4.9 KiB
		
	
	
	
		
			HTML
		
	
	
	
	
	
			
		
		
	
	
			153 lines
		
	
	
		
			4.9 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>
 | 
						|
	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>
 | 
						|
	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>
 |