/* Circle --------------------------------------------------------- */ private static void _fov_circle(fov_private_data_type data) { /* * Octants are defined by (x,y,r) where: * x = [p]ositive or [n]egative x increment * y = [p]ositive or [n]egative y increment * r = [y]es or [n]o for reflecting on axis x = y * * \pmy|ppy/ * \ | / * \ | / * mpn\|/ppn * ----@---- * mmn/|\pmn * / | \ * / | \ * /mmy|mpy\ */ fov_octant(data, 1, (float)0.0f, (float)1.0f, +1, +1, 'x', 'y', true, true); // PPN fov_octant(data, 1, (float)0.0f, (float)1.0f, +1, +1, 'y', 'x', true, false); // PPY fov_octant(data, 1, (float)0.0f, (float)1.0f, +1, -1, 'x', 'y', false, true); // PMN fov_octant(data, 1, (float)0.0f, (float)1.0f, +1, -1, 'y', 'x', false, false); // PMY fov_octant(data, 1, (float)0.0f, (float)1.0f, -1, +1, 'x', 'y', true, true); // MPN fov_octant(data, 1, (float)0.0f, (float)1.0f, -1, +1, 'y', 'x', true, false); // MPY fov_octant(data, 1, (float)0.0f, (float)1.0f, -1, -1, 'x', 'y', false, true); // MMN fov_octant(data, 1, (float)0.0f, (float)1.0f, -1, -1, 'y', 'x', false, false); // MMY }
/** * Calculate a full circle field of view from a source at (x,y). * * \param settings data structure containing settings. * \param map map data structure to be passed to callbacks. * \param source data structure holding source of light. * \param source_x x-axis coordinate from which to start. * \param source_y y-axis coordinate from which to start. * \param radius Euclidean distance from (x,y) after which to stop. */ public void fov_circle(fov_settings_type settings, map _map, Color source, int source_x, int source_y, uint radius) { fov_private_data_type data = new fov_private_data_type(); data.settings = settings; data._map = _map; data.source = source; data.source_x = source_x; data.source_y = source_y; data.radius = radius; _fov_circle(data); }
private static void _fov_octant_part(fov_octants_part part, fov_private_data_type data, int dx, float start_slope, float end_slope) { switch (part) { case fov_octants_part.FOV_OCTANT_PPN: fov_octant(data, dx, start_slope, end_slope, +1, +1, 'x', 'y', true, true); // PPN break; case fov_octants_part.FOV_OCTANT_PPY: fov_octant(data, dx, start_slope, end_slope, +1, +1, 'y', 'x', true, false); // PPY break; case fov_octants_part.FOV_OCTANT_PMN: fov_octant(data, dx, start_slope, end_slope, +1, -1, 'x', 'y', false, true); // PMN break; case fov_octants_part.FOV_OCTANT_PMY: fov_octant(data, dx, start_slope, end_slope, +1, -1, 'y', 'x', false, false); // PMY break; case fov_octants_part.FOV_OCTANT_MPN: fov_octant(data, dx, start_slope, end_slope, -1, +1, 'x', 'y', true, true); // MPN break; case fov_octants_part.FOV_OCTANT_MPY: fov_octant(data, dx, start_slope, end_slope, -1, +1, 'y', 'x', true, false); // MPY break; case fov_octants_part.FOV_OCTANT_MMN: fov_octant(data, dx, start_slope, end_slope, -1, -1, 'x', 'y', false, true); // MMN break; case fov_octants_part.FOV_OCTANT_MMY: fov_octant(data, dx, start_slope, end_slope, -1, -1, 'y', 'x', false, false); // MMY break; } }
/* Octants -------------------------------------------------------- */ private static void fov_octant( fov_private_data_type data, int dx, float start_slope, float end_slope, int signx, int signy, char rx, char ry, bool apply_edge, bool apply_diag) { int x = 0; int y = 0; int dy, dy0, dy1; uint h = 0; int prev_blocked = -1; float end_slope_next; fov_settings_type settings = data.settings; if (dx == 0) { fov_octant(data, dx + 1, start_slope, end_slope, signx, signy, rx, ry, apply_edge, apply_diag); return; } else if ((uint)dx > data.radius) { return; } dy0 = (int)(0.5f + ((float)dx) * start_slope); dy1 = (int)(0.5f + ((float)dx) * end_slope); if (rx == 'x') { x = data.source_x + signx * dx; } else if (rx == 'y') { y = data.source_y + signx * dx; } if (ry == 'y') { y = data.source_y + signy * dy0; } else if (ry == 'x') { x = data.source_x + signy * dy0; } if (!apply_diag && dy1 == dx) { /* We do diagonal lines on every second octant, so they don't get done twice. */ --dy1; } switch (settings.shape) { case fov_shape_type.FOV_SHAPE_CIRCLE_PRECALCULATE: h = height(settings, dx, data.radius); break; case fov_shape_type.FOV_SHAPE_CIRCLE: h = (uint)Mathf.Sqrt((float)(data.radius * data.radius - dx * dx)); break; case fov_shape_type.FOV_SHAPE_OCTAGON: h = (uint)(data.radius - dx) << 1; break; default: h = data.radius; break; } ; if ((uint)dy1 > h) { if (h == 0) { return; } dy1 = (int)h; } /*fprintf(stderr, "(%2d) = [%2d .. %2d] (%f .. %f), h=%d,edge=%d\n", * dx, dy0, dy1, ((float)dx)*start_slope, * 0.5f + ((float)dx)*end_slope, h, apply_edge);*/ for (dy = dy0; dy <= dy1; ++dy) { if (ry == 'y') { y = data.source_y + signy * dy; } else if (ry == 'x') { x = data.source_x + signy * dy; } if (settings.m_OnOpaqueEventHandler(data._map, x, y)) { if (settings.opaque_apply == fov_opaque_apply_type.FOV_OPAQUE_APPLY && (apply_edge || dy > 0)) { settings.m_OnApplyEventHandler(data._map, x, y, dx, dy, data.source); } if (prev_blocked == 0) { end_slope_next = fov_slope((float)dx + 0.5f, (float)dy - 0.5f); fov_octant(data, dx + 1, start_slope, end_slope_next, signx, signy, rx, ry, apply_edge, apply_diag); } prev_blocked = 1; } else { if (apply_edge || dy > 0) { settings.m_OnApplyEventHandler(data._map, x, y, dx, dy, data.source); } if (prev_blocked == 1) { start_slope = fov_slope((float)dx - 0.5f, (float)dy - 0.5f); } prev_blocked = 0; } } if (prev_blocked == 0) { fov_octant(data, dx + 1, start_slope, end_slope, signx, signy, rx, ry, apply_edge, apply_diag); } }
/** * Calculate a field of view from source at (x,y), pointing * in the given direction and with the given angle. The larger * the angle, the wider, "less focused" the beam. Each side of the * line pointing in the direction from the source will be half the * angle given such that the angle specified will be represented on * the raster. * * \param settings data structure containing settings. * \param map map data structure to be passed to callbacks. * \param source data structure holding source of light. * \param source_x x-axis coordinate from which to start. * \param source_y y-axis coordinate from which to start. * \param radius Euclidean distance from (x,y) after which to stop. * \param direction One of eight directions the beam of light can point. * \param angle The angle at the base of the beam of light, in degrees. */ public void fov_beam(fov_settings_type settings, map _map, Color source, int source_x, int source_y, uint radius, fov_direction_type direction, float angle) { fov_private_data_type data = new fov_private_data_type(); float start_slope, end_slope, a; data.settings = settings; data._map = _map; data.source = source; data.source_x = source_x; data.source_y = source_y; data.radius = radius; if (angle <= 0.0f) { return; } else if (angle >= 360.0f) { _fov_circle(data); } /* Calculate the angle as a percentage of 45 degrees, halved (for * each side of the centre of the beam). e.g. angle = 180.0f means * half the beam is 90.0 which is 2x45, so the result is 2.0. */ a = angle / 90.0f; if (direction == fov_direction_type.FOV_EAST) { end_slope = betweenf(a, 0.0f, 1.0f); _fov_octant_part(fov_octants_part.FOV_OCTANT_PPN, data, 1, 0.0f, end_slope); _fov_octant_part(fov_octants_part.FOV_OCTANT_PMN, data, 1, 0.0f, end_slope); if (a - 1.0f > Mathf.Epsilon) { /* a > 1.0f */ start_slope = betweenf(2.0f - a, 0.0f, 1.0f); _fov_octant_part(fov_octants_part.FOV_OCTANT_PPY, data, 1, start_slope, 1.0f); _fov_octant_part(fov_octants_part.FOV_OCTANT_MPY, data, 1, start_slope, 1.0f); } if (a - 2.0f > Mathf.Epsilon) { /* a > 2.0f */ end_slope = betweenf(a - 2.0f, 0.0f, 1.0f); _fov_octant_part(fov_octants_part.FOV_OCTANT_PMY, data, 1, 0.0f, end_slope); _fov_octant_part(fov_octants_part.FOV_OCTANT_MMY, data, 1, 0.0f, end_slope); } if (a - 3.0f > Mathf.Epsilon) { /* a > 3.0f */ start_slope = betweenf(4.0f - a, 0.0f, 1.0f); _fov_octant_part(fov_octants_part.FOV_OCTANT_MPN, data, 1, start_slope, 1.0f); _fov_octant_part(fov_octants_part.FOV_OCTANT_MMN, data, 1, start_slope, 1.0f); } } if (direction == fov_direction_type.FOV_WEST) { end_slope = betweenf(a, 0.0f, 1.0f); _fov_octant_part(fov_octants_part.FOV_OCTANT_MPN, data, 1, 0.0f, end_slope); _fov_octant_part(fov_octants_part.FOV_OCTANT_MMN, data, 1, 0.0f, end_slope); if (a - 1.0f > Mathf.Epsilon) { /* a > 1.0f */ start_slope = betweenf(2.0f - a, 0.0f, 1.0f); _fov_octant_part(fov_octants_part.FOV_OCTANT_PMY, data, 1, start_slope, 1.0f); _fov_octant_part(fov_octants_part.FOV_OCTANT_MMY, data, 1, start_slope, 1.0f); } if (a - 2.0f > Mathf.Epsilon) { /* a > 2.0f */ end_slope = betweenf(a - 2.0f, 0.0f, 1.0f); _fov_octant_part(fov_octants_part.FOV_OCTANT_PPY, data, 1, 0.0f, end_slope); _fov_octant_part(fov_octants_part.FOV_OCTANT_MPY, data, 1, 0.0f, end_slope); } if (a - 3.0f > Mathf.Epsilon) { /* a > 3.0f */ start_slope = betweenf(4.0f - a, 0.0f, 1.0f); _fov_octant_part(fov_octants_part.FOV_OCTANT_PPN, data, 1, start_slope, 1.0f); _fov_octant_part(fov_octants_part.FOV_OCTANT_PMN, data, 1, start_slope, 1.0f); } } if (direction == fov_direction_type.FOV_NORTH) { end_slope = betweenf(a, 0.0f, 1.0f); _fov_octant_part(fov_octants_part.FOV_OCTANT_MPY, data, 1, 0.0f, end_slope); _fov_octant_part(fov_octants_part.FOV_OCTANT_MMY, data, 1, 0.0f, end_slope); if (a - 1.0f > Mathf.Epsilon) { /* a > 1.0f */ start_slope = betweenf(2.0f - a, 0.0f, 1.0f); _fov_octant_part(fov_octants_part.FOV_OCTANT_MMN, data, 1, start_slope, 1.0f); _fov_octant_part(fov_octants_part.FOV_OCTANT_PMN, data, 1, start_slope, 1.0f); } if (a - 2.0f > Mathf.Epsilon) { /* a > 2.0f */ end_slope = betweenf(a - 2.0f, 0.0f, 1.0f); _fov_octant_part(fov_octants_part.FOV_OCTANT_MPN, data, 1, 0.0f, end_slope); _fov_octant_part(fov_octants_part.FOV_OCTANT_PPN, data, 1, 0.0f, end_slope); } if (a - 3.0f > Mathf.Epsilon) { /* a > 3.0f */ start_slope = betweenf(4.0f - a, 0.0f, 1.0f); _fov_octant_part(fov_octants_part.FOV_OCTANT_PMY, data, 1, start_slope, 1.0f); _fov_octant_part(fov_octants_part.FOV_OCTANT_PPY, data, 1, start_slope, 1.0f); } } if (direction == fov_direction_type.FOV_SOUTH) { end_slope = betweenf(a, 0.0f, 1.0f); _fov_octant_part(fov_octants_part.FOV_OCTANT_PMY, data, 1, 0.0f, end_slope); _fov_octant_part(fov_octants_part.FOV_OCTANT_PPY, data, 1, 0.0f, end_slope); if (a - 1.0f > Mathf.Epsilon) { /* a > 1.0f */ start_slope = betweenf(2.0f - a, 0.0f, 1.0f); _fov_octant_part(fov_octants_part.FOV_OCTANT_MPN, data, 1, start_slope, 1.0f); _fov_octant_part(fov_octants_part.FOV_OCTANT_PPN, data, 1, start_slope, 1.0f); } if (a - 2.0f > Mathf.Epsilon) { /* a > 2.0f */ end_slope = betweenf(a - 2.0f, 0.0f, 1.0f); _fov_octant_part(fov_octants_part.FOV_OCTANT_MMN, data, 1, 0.0f, end_slope); _fov_octant_part(fov_octants_part.FOV_OCTANT_PMN, data, 1, 0.0f, end_slope); } if (a - 3.0f > Mathf.Epsilon) { /* a > 3.0f */ start_slope = betweenf(4.0f - a, 0.0f, 1.0f); _fov_octant_part(fov_octants_part.FOV_OCTANT_MMY, data, 1, start_slope, 1.0f); _fov_octant_part(fov_octants_part.FOV_OCTANT_MPY, data, 1, start_slope, 1.0f); } } if (direction == fov_direction_type.FOV_NORTHEAST) { start_slope = betweenf(1.0f - a, 0.0f, 1.0f); _fov_octant_part(fov_octants_part.FOV_OCTANT_PMN, data, 1, start_slope, 1.0f); _fov_octant_part(fov_octants_part.FOV_OCTANT_MPY, data, 1, start_slope, 1.0f); if (a - 1.0f > Mathf.Epsilon) { /* a > 1.0f */ end_slope = betweenf(a - 1.0f, 0.0f, 1.0f); _fov_octant_part(fov_octants_part.FOV_OCTANT_MMY, data, 1, 0.0f, end_slope); _fov_octant_part(fov_octants_part.FOV_OCTANT_PPN, data, 1, 0.0f, end_slope); } if (a - 2.0f > Mathf.Epsilon) { /* a > 2.0f */ start_slope = betweenf(3.0f - a, 0.0f, 1.0f); _fov_octant_part(fov_octants_part.FOV_OCTANT_MMN, data, 1, start_slope, 1.0f); _fov_octant_part(fov_octants_part.FOV_OCTANT_PPY, data, 1, start_slope, 1.0f); } if (a - 3.0f > Mathf.Epsilon) { /* a > 3.0f */ end_slope = betweenf(a - 3.0f, 0.0f, 1.0f); _fov_octant_part(fov_octants_part.FOV_OCTANT_MPN, data, 1, 0.0f, end_slope); _fov_octant_part(fov_octants_part.FOV_OCTANT_PMY, data, 1, 0.0f, end_slope); } } if (direction == fov_direction_type.FOV_NORTHWEST) { start_slope = betweenf(1.0f - a, 0.0f, 1.0f); _fov_octant_part(fov_octants_part.FOV_OCTANT_MMN, data, 1, start_slope, 1.0f); _fov_octant_part(fov_octants_part.FOV_OCTANT_MMY, data, 1, start_slope, 1.0f); if (a - 1.0f > Mathf.Epsilon) { /* a > 1.0f */ end_slope = betweenf(a - 1.0f, 0.0f, 1.0f); _fov_octant_part(fov_octants_part.FOV_OCTANT_MPN, data, 1, 0.0f, end_slope); _fov_octant_part(fov_octants_part.FOV_OCTANT_MPY, data, 1, 0.0f, end_slope); } if (a - 2.0f > Mathf.Epsilon) { /* a > 2.0f */ start_slope = betweenf(3.0f - a, 0.0f, 1.0f); _fov_octant_part(fov_octants_part.FOV_OCTANT_PMY, data, 1, start_slope, 1.0f); _fov_octant_part(fov_octants_part.FOV_OCTANT_PMN, data, 1, start_slope, 1.0f); } if (a - 3.0f > Mathf.Epsilon) { /* a > 3.0f */ end_slope = betweenf(a - 3.0f, 0.0f, 1.0f); _fov_octant_part(fov_octants_part.FOV_OCTANT_PPY, data, 1, 0.0f, end_slope); _fov_octant_part(fov_octants_part.FOV_OCTANT_PPN, data, 1, 0.0f, end_slope); } } if (direction == fov_direction_type.FOV_SOUTHEAST) { start_slope = betweenf(1.0f - a, 0.0f, 1.0f); _fov_octant_part(fov_octants_part.FOV_OCTANT_PPN, data, 1, start_slope, 1.0f); _fov_octant_part(fov_octants_part.FOV_OCTANT_PPY, data, 1, start_slope, 1.0f); if (a - 1.0f > Mathf.Epsilon) { /* a > 1.0f */ end_slope = betweenf(a - 1.0f, 0.0f, 1.0f); _fov_octant_part(fov_octants_part.FOV_OCTANT_PMY, data, 1, 0.0f, end_slope); _fov_octant_part(fov_octants_part.FOV_OCTANT_PMN, data, 1, 0.0f, end_slope); } if (a - 2.0f > Mathf.Epsilon) { /* a > 2.0f */ start_slope = betweenf(3.0f - a, 0.0f, 1.0f); _fov_octant_part(fov_octants_part.FOV_OCTANT_MPN, data, 1, start_slope, 1.0f); _fov_octant_part(fov_octants_part.FOV_OCTANT_MPY, data, 1, start_slope, 1.0f); } if (a - 3.0f > Mathf.Epsilon) { /* a > 3.0f */ end_slope = betweenf(a - 3.0f, 0.0f, 1.0f); _fov_octant_part(fov_octants_part.FOV_OCTANT_MMN, data, 1, 0.0f, end_slope); _fov_octant_part(fov_octants_part.FOV_OCTANT_MMY, data, 1, 0.0f, end_slope); } } if (direction == fov_direction_type.FOV_SOUTHWEST) { start_slope = betweenf(1.0f - a, 0.0f, 1.0f); _fov_octant_part(fov_octants_part.FOV_OCTANT_PMY, data, 1, start_slope, 1.0f); _fov_octant_part(fov_octants_part.FOV_OCTANT_MPN, data, 1, start_slope, 1.0f); if (a - 1.0f > Mathf.Epsilon) { /* a > 1.0f */ end_slope = betweenf(a - 1.0f, 0.0f, 1.0f); _fov_octant_part(fov_octants_part.FOV_OCTANT_PPY, data, 1, 0.0f, end_slope); _fov_octant_part(fov_octants_part.FOV_OCTANT_MMN, data, 1, 0.0f, end_slope); } if (a - 2.0f > Mathf.Epsilon) { /* a > 2.0f */ start_slope = betweenf(3.0f - a, 0.0f, 1.0f); _fov_octant_part(fov_octants_part.FOV_OCTANT_PPN, data, 1, start_slope, 1.0f); _fov_octant_part(fov_octants_part.FOV_OCTANT_MMY, data, 1, start_slope, 1.0f); } if (a - 3.0f > Mathf.Epsilon) { /* a > 3.0f */ end_slope = betweenf(a - 3.0f, 0.0f, 1.0f); _fov_octant_part(fov_octants_part.FOV_OCTANT_PMN, data, 1, 0.0f, end_slope); _fov_octant_part(fov_octants_part.FOV_OCTANT_MPY, data, 1, 0.0f, end_slope); } } }