Module: duim-extended-geometry-internals Synopsis: DUIM extended geometry Author: Scott McKay, Andy Armstrong Copyright: Original Code is Copyright (c) 1995-2004 Functional Objects, Inc. All rights reserved. License: Functional Objects Library Public License Version 1.0 Dual-license: GNU Lesser General Public License Warranty: Distributed WITHOUT WARRANTY OF ANY KIND /// Polygons and polylines define protocol <> (<>) function polygon-points (polygon) => (points :: ); function do-polygon-coordinates (function :: , polygon) => (); function do-polygon-segments (function :: , polygon) => (); function polyline-closed? (polyline) => (true? :: ); end protocol <>; define abstract class () sealed slot %coords :: false-or() = #f, init-keyword: coordinates:; sealed slot %points :: false-or() = #f, init-keyword: points:; end class ; define method polygon-points (polygon :: ) => (points :: ) polygon.%points | begin // If %points empty, %coords will not be let coords = polygon.%coords; let npoints :: = truncate/(size(coords), 2); let points :: = make(, size: npoints); without-bounds-checks for (i :: from 0 below npoints) points[i] := make-point(coords[i * 2 + 0], coords[i * 2 + 1]) end end; polygon.%points := points end end method polygon-points; define method polygon-coordinates (polygon :: ) => (coords :: ) polygon.%coords | begin // If %coords empty, %points will not be let points = polygon.%points; let npoints :: = size(points); let coords :: = make(, size: npoints * 2); without-bounds-checks for (i :: from 0 below npoints) coords[i * 2 + 0] := point-x(points[i]); coords[i * 2 + 1] := point-y(points[i]) end end; polygon.%coords := coords end end method polygon-coordinates; define method do-polygon-coordinates (function :: , polygon :: ) => () if (polygon.%coords) let coords = polygon.%coords; let ncoords :: = size(coords) - 1; let i :: = -1; until (i = ncoords) function(coords[inc!(i)], coords[inc!(i)]) end else local method do-coords (point) => () function(point-x(point), point-y(point)) end method; do(do-coords, polygon.%points) end end method do-polygon-coordinates; define method do-polygon-segments (function :: , polygon :: ) => () if (polygon.%coords) let coords = polygon.%coords; let ncoords :: = size(coords) - 1; let x1 = coords[0]; let y1 = coords[1]; let x = x1; let y = y1; let i :: = 1; until (i = ncoords) function(x, y, x := coords[inc!(i)], y := coords[inc!(i)]) end; when (polyline-closed?(polygon)) function(x, y, x1, y1) end else let (x1, y1) = point-position(polygon.%points[0]); let x = x1; let y = y1; let points = polygon.%points; let npoints :: = size(points); for (i :: from 0 below npoints - 1) let (nx, ny) = point-position(points[i + 1]); function(x, y, nx, ny); x := nx; y := ny end; when (polyline-closed?(polygon)) function(x, y, x1, y1) end end end method do-polygon-segments; define method box-edges (polygon :: ) => (left :: , top :: , right :: , bottom :: ) let min-x = $largest-coordinate; let min-y = $largest-coordinate; let max-x = $smallest-coordinate; let max-y = $smallest-coordinate; local method add-coord (x, y) => () min!(min-x, x); min!(min-y, y); max!(max-x, x); max!(max-y, y) end method; do-polygon-coordinates(add-coord, polygon); fix-box(min-x, min-y, max-x, max-y) end method box-edges; define sealed class (, ) sealed constant slot polyline-closed? :: = #f, init-keyword: closed?:; end class ; define sealed domain make (singleton()); define sealed domain initialize (); define inline function make-polyline (coord-seq, #key closed?) => (polyline :: ) assert(even?(size(coord-seq)), "There must be an even number of coordinates in %=", coord-seq); make(, coordinates: as(, coord-seq), closed?: closed?) end function make-polyline; define inline function make-polyline* (point-seq, #key closed?) => (polyline :: ) make(, points: as(, point-seq), closed?: closed?) end function make-polyline*; define sealed inline method make (class == , #key points, closed?) => (polyline :: ) make-polyline*(points, closed?: closed?) end method make; define method transform-region (transform :: , polyline :: ) => (polyline :: ) let coords :: = make(); local method transform-coord (x, y) => () let (nx, ny) = transform-position(transform, x, y); add!(coords, ny); add!(coords, nx) end method; do-polygon-coordinates(transform-coord, polyline); make-polyline(coords, closed?: polyline-closed?(polyline)) end method transform-region; define method region-equal (p1 :: , p2 :: ) => (true? :: ) polygon-coordinates(p1) = polygon-coordinates(p2) & polyline-closed?(p1) = polyline-closed?(p2) end method region-equal; define sealed class (, ) end class ; define sealed domain make (singleton()); define sealed domain initialize (); define inline function make-polygon (coord-seq) => (polygon :: ) assert(even?(size(coord-seq)), "There must be an even number of coordinates in %=", coord-seq); make(, coordinates: as(, coord-seq)) end function make-polygon; define inline function make-polygon* (point-seq) => (polygon :: ) make(, points: as(, point-seq)) end function make-polygon*; define sealed inline method make (class == , #key points) => (polygon :: ) make-polygon*(points); end method make; define method polyline-closed? (polygon :: ) => (true? :: ) #t end method polyline-closed?; define method transform-region (transform :: , polygon :: ) => (polygon :: ) let coords :: = make(); local method transform-coord (x, y) => () let (nx, ny) = transform-position(transform, x, y); add!(coords, ny); add!(coords, nx) end method; do-polygon-coordinates(transform-coord, polygon); make-polygon(coords) end method transform-region; define method region-equal (p1 :: , p2 :: ) => (true? :: ) polygon-coordinates(p1) = polygon-coordinates(p2) end method region-equal; define method region-contains-position? (polygon :: , x :: , y :: ) => (true? :: ) let (left, top, right, bottom) = box-edges(polygon); ltrb-contains-position?(left, top, right, bottom, fix-coordinate(x), fix-coordinate(y)) & position-inside-polygon?(x, y, polygon-coordinates(polygon), closed?: #t) end method region-contains-position?;