356 lines
8.9 KiB
C++
356 lines
8.9 KiB
C++
/* ScummVM - Graphic Adventure Engine
|
|
*
|
|
* ScummVM is the legal property of its developers, whose names
|
|
* are too numerous to list here. Please refer to the COPYRIGHT
|
|
* file distributed with this source distribution.
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
*/
|
|
|
|
#include "qdengine/qd_fwd.h"
|
|
#include "qdengine/parser/qdscr_parser.h"
|
|
#include "qdengine/parser/xml_tag_buffer.h"
|
|
#include "qdengine/qdcore/qd_contour.h"
|
|
|
|
namespace QDEngine {
|
|
|
|
qdContour::qdContour(qdContourType tp) : _contour_type(tp),
|
|
_size(0, 0),
|
|
_mask_pos(0, 0) {
|
|
}
|
|
|
|
qdContour::qdContour(const qdContour &ct) : _contour_type(ct._contour_type),
|
|
_size(ct._size),
|
|
_contour(ct._contour),
|
|
_mask(ct._mask) {
|
|
_mask_pos = ct._mask_pos;
|
|
}
|
|
|
|
qdContour::~qdContour() {
|
|
_contour.clear();
|
|
}
|
|
|
|
qdContour &qdContour::operator = (const qdContour &ct) {
|
|
if (this == &ct) return *this;
|
|
|
|
_contour_type = ct._contour_type;
|
|
_size = ct._size;
|
|
_mask_pos = ct._mask_pos;
|
|
|
|
_mask = ct._mask;
|
|
|
|
_contour = ct._contour;
|
|
|
|
return *this;
|
|
}
|
|
|
|
void qdContour::add_contour_point(const Vect2s &pt) {
|
|
_contour.push_back(pt);
|
|
}
|
|
|
|
void qdContour::insert_contour_point(const Vect2s &pt, int insert_pos) {
|
|
if (insert_pos < (int)_contour.size()) {
|
|
if (insert_pos < 0) insert_pos = 0;
|
|
_contour.insert(_contour.begin() + insert_pos, pt);
|
|
} else {
|
|
_contour.push_back(pt);
|
|
}
|
|
}
|
|
|
|
bool qdContour::remove_contour_point(int pos) {
|
|
if (pos >= 0 && pos < (int)_contour.size()) {
|
|
_contour.erase(_contour.begin() + pos);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool qdContour::update_contour_point(const Vect2s &pt, int pos) {
|
|
if (pos >= 0 && pos < (int)_contour.size()) {
|
|
_contour[pos] = pt;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void qdContour::createMaskOld(int x0, int y0, int x1, int y1) {
|
|
_mask.resize(_size.x * _size.y);
|
|
Common::fill(_mask.begin(), _mask.end(), 0);
|
|
|
|
Std::vector<int> intersections;
|
|
intersections.reserve(_contour.size());
|
|
|
|
Std::vector<byte>::iterator it = _mask.begin();
|
|
for (int y = y0; y <= y1; y++) {
|
|
byte *ptr = &*it;
|
|
|
|
for (uint i = 0; i < _contour.size(); i++) {
|
|
Vect2s p0 = _contour[i];
|
|
Vect2s p1 = (i < _contour.size() - 1) ? _contour[i + 1] : _contour[0];
|
|
|
|
if (p0.y != p1.y) {
|
|
if (((p0.y << 2) <= (y << 2) + 2 && (p1.y << 2) >= (y << 2) + 2) || ((p0.y << 2) >= (y << 2) + 2 && (p1.y << 2) <= (y << 2) + 2))
|
|
intersections.push_back((y - p0.y) * (p1.x - p0.x) / (p1.y - p0.y) + p0.x);
|
|
}
|
|
}
|
|
|
|
if (intersections.size() > 1) {
|
|
Common::sort(intersections.begin(), intersections.end());
|
|
for (uint i = 0; i < intersections.size() - 1; i += 2) {
|
|
int xl = intersections[i];
|
|
int xr = intersections[i + 1];
|
|
|
|
if (xr > xl) {
|
|
byte *p = ptr + xl - x0;
|
|
for (int col = xl; col <= xr; col++)
|
|
*p++ = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
intersections.clear();
|
|
|
|
for (uint i = 0; i < _contour.size(); i++) {
|
|
Vect2s p0 = _contour[i];
|
|
Vect2s p1 = (i < _contour.size() - 1) ? _contour[i + 1] : _contour[0];
|
|
|
|
if (p0.y != p1.y) {
|
|
if (((p0.y << 2) <= (y << 2) - 2 && (p1.y << 2) >= (y << 2) - 2) || ((p0.y << 2) >= (y << 2) - 2 && (p1.y << 2) <= (y << 2) - 2))
|
|
intersections.push_back((y - p0.y) * (p1.x - p0.x) / (p1.y - p0.y) + p0.x);
|
|
}
|
|
}
|
|
|
|
if (intersections.size() > 1) {
|
|
Common::sort(intersections.begin(), intersections.end());
|
|
for (uint i = 0; i < intersections.size() - 1; i += 2) {
|
|
int xl = intersections[i];
|
|
int xr = intersections[i + 1];
|
|
|
|
if (xr > xl) {
|
|
byte *p = ptr + xl - x0;
|
|
for (int col = xl; col <= xr; col++)
|
|
*p++ = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
intersections.clear();
|
|
|
|
it += _size.x;
|
|
}
|
|
}
|
|
|
|
bool qdContour::update_contour() {
|
|
if (_contour_type != CONTOUR_POLYGON) return false;
|
|
|
|
if (_contour.empty())
|
|
return false;
|
|
|
|
int x0 = _contour[0].x;
|
|
int x1 = _contour[0].x;
|
|
int y0 = _contour[0].y;
|
|
int y1 = _contour[0].y;
|
|
|
|
for (uint i = 0; i < _contour.size(); i++) {
|
|
if (_contour[i].x < x0) x0 = _contour[i].x;
|
|
if (_contour[i].x > x1) x1 = _contour[i].x;
|
|
if (_contour[i].y < y0) y0 = _contour[i].y;
|
|
if (_contour[i].y > y1) y1 = _contour[i].y;
|
|
}
|
|
|
|
_size = Vect2s(x1 - x0 + 1, y1 - y0 + 1);
|
|
_mask_pos = Vect2s(x0 + _size.x / 2, y0 + _size.y / 2);
|
|
|
|
if (g_engine->_gameVersion <= 20050101)
|
|
createMaskOld(x0, y0, x1, y1);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool qdContour::is_inside(const Vect2s &pos) const {
|
|
switch (_contour_type) {
|
|
case CONTOUR_RECTANGLE:
|
|
if (pos.x >= -_size.x / 2 && pos.x < _size.x / 2 && pos.y >= -_size.y / 2 && pos.y < _size.y / 2)
|
|
return true;
|
|
break;
|
|
case CONTOUR_CIRCLE:
|
|
if (pos.x * pos.x + pos.y * pos.y <= _size.x * _size.x)
|
|
return true;
|
|
break;
|
|
case CONTOUR_POLYGON: {
|
|
if (g_engine->_gameVersion > 20050101) {
|
|
Vect2s p = pos;
|
|
int intersections_lt0 = 0;
|
|
int intersections_gt0 = 0;
|
|
int intersections_lt1 = 0;
|
|
int intersections_gt1 = 0;
|
|
for (uint i = 0; i < _contour.size(); i ++) {
|
|
Vect2s p0 = _contour[i];
|
|
Vect2s p1 = (i < _contour.size() - 1) ? _contour[i + 1] : _contour[0];
|
|
if (p0.y != p1.y) {
|
|
if ((p0.y < p.y && p1.y >= p.y) || (p0.y >= p.y && p1.y < p.y)) {
|
|
if (p0.x < p.x && p1.x < p.x)
|
|
intersections_lt0++;
|
|
else if (p0.x > p.x && p1.x > p.x)
|
|
intersections_gt0++;
|
|
else {
|
|
int x = (p.y - p0.y) * (p1.x - p0.x) / (p1.y - p0.y) + p0.x;
|
|
|
|
if (x == p.x)
|
|
return true;
|
|
else if (x > p.x)
|
|
intersections_gt0++;
|
|
else
|
|
intersections_lt0++;
|
|
}
|
|
}
|
|
if ((p0.y <= p.y && p1.y > p.y) || (p0.y > p.y && p1.y <= p.y)) {
|
|
if (p0.x < p.x && p1.x < p.x)
|
|
intersections_lt1++;
|
|
else if (p0.x > p.x && p1.x > p.x)
|
|
intersections_gt1++;
|
|
else {
|
|
int x = (p.y - p0.y) * (p1.x - p0.x) / (p1.y - p0.y) + p0.x;
|
|
|
|
if (x == p.x)
|
|
return true;
|
|
else if (x > p.x)
|
|
intersections_gt1++;
|
|
else
|
|
intersections_lt1++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return ((intersections_lt0 & 1) && intersections_gt0 != 0) ||
|
|
((intersections_lt1 & 1) && intersections_gt1 != 0);
|
|
} else {
|
|
Vect2s p = pos - _mask_pos;
|
|
p.x += _size.x / 2;
|
|
p.y += _size.y / 2;
|
|
if (p.x >= 0 && p.x < _size.x && p.y >= 0 && p.y < _size.y) {
|
|
if (_mask[p.x + p.y * _size.x])
|
|
return true;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool qdContour::save_script(Common::WriteStream &fh, int indent) const {
|
|
if (_contour_type == CONTOUR_POLYGON) {
|
|
for (int i = 0; i < indent; i++) {
|
|
fh.writeString("\t");
|
|
}
|
|
fh.writeString(Common::String::format("<contour_polygon>%d", contour_size() * 2));
|
|
for (int j = 0; j < contour_size(); j++) {
|
|
fh.writeString(Common::String::format(" %d %d", _contour[j].x, _contour[j].y));
|
|
}
|
|
fh.writeString("</contour_polygon>\r\n");
|
|
return true;
|
|
}
|
|
|
|
if (_contour_type == CONTOUR_RECTANGLE) {
|
|
for (int i = 0; i < indent; i++) {
|
|
fh.writeString("\t");
|
|
}
|
|
fh.writeString(Common::String::format("<contour_rect>%d %d</contour_rect>\r\n", _size.x, _size.y));
|
|
return true;
|
|
}
|
|
|
|
if (_contour_type == CONTOUR_CIRCLE) {
|
|
for (int i = 0; i < indent; i++) {
|
|
fh.writeString("\t");
|
|
}
|
|
fh.writeString(Common::String::format("<contour_circle>%d</contour_circle>\r\n", _size.x));
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool qdContour::load_script(const xml::tag *p) {
|
|
int i;
|
|
|
|
Vect2s v;
|
|
xml::tag_buffer buf(*p);
|
|
|
|
switch (_contour_type) {
|
|
case CONTOUR_RECTANGLE:
|
|
buf > v.x > v.y;
|
|
_size = Vect2s(v);
|
|
return true;
|
|
case CONTOUR_CIRCLE:
|
|
_size.x = _size.y = buf.get_short();
|
|
return true;
|
|
case CONTOUR_POLYGON:
|
|
_contour.reserve(p->data_size() / 2);
|
|
for (i = 0; i < p->data_size(); i += 2) {
|
|
buf > v.x > v.y;
|
|
add_contour_point(v);
|
|
}
|
|
update_contour();
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void qdContour::divide_contour(int shift) {
|
|
Std::vector<Vect2s>::iterator _itr = _contour.begin(), _end = _contour.end();
|
|
for (; _itr != _end; ++_itr) {
|
|
Vect2s &v = *_itr;
|
|
v.x >>= shift;
|
|
v.y >>= shift;
|
|
}
|
|
}
|
|
|
|
void qdContour::mult_contour(int shift) {
|
|
Std::vector<Vect2s>::iterator _itr = _contour.begin(), _end = _contour.end();
|
|
for (; _itr != _end; ++_itr) {
|
|
Vect2s &v = *_itr;
|
|
v.x <<= shift;
|
|
v.y <<= shift;
|
|
}
|
|
}
|
|
|
|
void qdContour::shift_contour(int dx, int dy) {
|
|
Std::vector<Vect2s>::iterator _itr = _contour.begin(), _end = _contour.end();
|
|
for (; _itr != _end; ++_itr) {
|
|
Vect2s &v = *_itr;
|
|
v.x += dx;
|
|
v.y += dy;
|
|
}
|
|
}
|
|
|
|
bool qdContour::can_be_closed() const {
|
|
if (_contour_type != CONTOUR_POLYGON)
|
|
return false;
|
|
|
|
return (_contour.size() > 2);
|
|
}
|
|
|
|
bool qdContour::is_contour_empty() const {
|
|
if (_contour_type == CONTOUR_POLYGON)
|
|
return (_contour.size() < 3);
|
|
|
|
return false;
|
|
}
|
|
|
|
} // namespace QDEngine
|