Skip to content

Commit

Permalink
Scripting: Added Geometry interface with line and ellipse helpers (#3779
Browse files Browse the repository at this point in the history
)

Providing access from scripts to the existing line and ellipse rendering
functions.
  • Loading branch information
bjorn committed Aug 1, 2023
1 parent 90d8da3 commit 4a0f569
Show file tree
Hide file tree
Showing 10 changed files with 209 additions and 4 deletions.
1 change: 1 addition & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
* Scripting: Added API for editing tile layers using terrain sets (with a-morphous, #3758)
* Scripting: Added file dialog API (with dogboydog, #3782)
* Scripting: Support erasing tiles in Tool.preview and TileMap.merge
* Scripting: Added Geometry interface with line and ellipse helpers
* Scripting: Added WangSet.effectiveTypeForColor
* Fixed crash when changing file property of custom class (#3783)
* Fixed object preview position with parallax factor on group layer (#3669)
Expand Down
34 changes: 34 additions & 0 deletions docs/scripting-doc/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1507,6 +1507,40 @@ declare namespace Base64 {
export function decode(data: ArrayBuffer | string): ArrayBuffer;
}

/**
* Provides functions to rasterize lines and ellipses.
*
* @since 1.10.2
*/
declare namespace Geometry {
/**
* Returns the lists of points on a line from `a` to `b`.
*
* When the `manhattan` option (named after "Manhattan distance") is set to
* `true`, the points on the line can't take diagonal steps.
*/
export function pointsOnLine(a: point, b: point, manhattan?: boolean): point[];

/**
* Returns a lists of points on an ellipse, with `center` as the midpoint
* and with the given radii.
*
* May return duplicate points.
*/
export function pointsOnEllipse(center: point, radiusX: number, radiusY: number): point[];

/**
* Returns an elliptical region based on the given bounding rectangle.
*/
export function ellipseRegion(rect: rect): region

/**
* Returns an elliptical region based on a bounding rectangle given by x0,y0
* (top-left) and x1,y1 (bottom-right), inclusive.
*/
export function ellipseRegion(x0: number, y0: number, x1: number, y1: number): region
}

/**
* Offers various operations on file paths, such as turning absolute paths
* into relative ones, splitting a path into its components, and so on.
Expand Down
9 changes: 9 additions & 0 deletions src/tiled/editablemapobject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ EditableMapObject::~EditableMapObject()
EditableManager::instance().remove(this);
}

#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
QJSValue EditableMapObject::polygon() const
{
QJSEngine *engine = ScriptManager::instance().engine();
Expand All @@ -77,6 +78,7 @@ QJSValue EditableMapObject::polygon() const

return array;
}
#endif

EditableTile *EditableMapObject::tile() const
{
Expand Down Expand Up @@ -166,6 +168,7 @@ void EditableMapObject::setVisible(bool visible)
setMapObjectProperty(MapObject::VisibleProperty, visible);
}

#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
void EditableMapObject::setPolygon(QJSValue polygonValue)
{
if (!polygonValue.isArray()) {
Expand All @@ -189,6 +192,12 @@ void EditableMapObject::setPolygon(QJSValue polygonValue)
polygon.append(point);
}

setPolygon(polygon);
}
#endif

void EditableMapObject::setPolygon(const QPolygonF &polygon)
{
if (Document *doc = document()) {
asset()->push(new ChangePolygon(doc, mapObject(), polygon));
} else if (!checkReadOnly()) {
Expand Down
18 changes: 18 additions & 0 deletions src/tiled/editablemapobject.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,11 @@ class EditableMapObject : public EditableObject
Q_PROPERTY(QSizeF size READ size WRITE setSize)
Q_PROPERTY(qreal rotation READ rotation WRITE setRotation)
Q_PROPERTY(bool visible READ isVisible WRITE setVisible)
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
Q_PROPERTY(QJSValue polygon READ polygon WRITE setPolygon)
#else
Q_PROPERTY(QList<QPointF> polygon READ polygon WRITE setPolygon)
#endif
Q_PROPERTY(QString text READ text WRITE setText)
Q_PROPERTY(Tiled::Font font READ font WRITE setFont)
Q_PROPERTY(Qt::Alignment textAlignment READ textAlignment WRITE setTextAlignment)
Expand Down Expand Up @@ -115,7 +119,11 @@ class EditableMapObject : public EditableObject
QSizeF size() const;
qreal rotation() const;
bool isVisible() const;
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
QJSValue polygon() const;
#else
const QPolygonF &polygon() const;
#endif
QString text() const;
Font font() const;
Qt::Alignment textAlignment() const;
Expand Down Expand Up @@ -146,7 +154,10 @@ public slots:
void setSize(QSizeF size);
void setRotation(qreal rotation);
void setVisible(bool visible);
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
void setPolygon(QJSValue polygon);
#endif
void setPolygon(const QPolygonF &polygon);
void setText(const QString &text);
void setFont(const Font &font);
void setTextAlignment(Qt::Alignment textAlignment);
Expand Down Expand Up @@ -219,6 +230,13 @@ inline bool EditableMapObject::isVisible() const
return mapObject()->isVisible();
}

#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
inline const QPolygonF &EditableMapObject::polygon() const
{
return mapObject()->polygon();
}
#endif

inline QString EditableMapObject::text() const
{
return mapObject()->textData().text;
Expand Down
11 changes: 11 additions & 0 deletions src/tiled/geometry.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,17 @@ QRegion ellipseRegion(int x0, int y0, int x1, int y1)
return ret;
}

QRegion ellipseRegion(QRect rect)
{
// Check for empty rectangle explicitly, because ellipseRegion above can't
// handle empty rectangles due to the coordinates being inclusive.
if (rect.width() == 0 || rect.height() == 0)
return QRegion();

rect = rect.normalized();
return ellipseRegion(rect.left(), rect.top(), rect.right(), rect.bottom());
}

/**
* Returns the lists of points on a line from (x0,y0) to (x1,y1).
*
Expand Down
4 changes: 1 addition & 3 deletions src/tiled/geometry.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,12 @@ namespace Tiled {

QVector<QPoint> pointsOnEllipse(int xm, int ym, int a, int b);
QRegion ellipseRegion(int x0, int y0, int x1, int y1);
QRegion ellipseRegion(QRect rect);
QVector<QPoint> pointsOnLine(int x0, int y0, int x1, int y1, bool manhattan = false);

inline QVector<QPoint> pointsOnEllipse(QPoint center, int radiusX, int radiusY)
{ return pointsOnEllipse(center.x(), center.y(), radiusX, radiusY); }

inline QRegion ellipseRegion(QRect rect)
{ return ellipseRegion(rect.left(), rect.top(), rect.right(), rect.bottom()); }

inline QVector<QPoint> pointsOnLine(QPoint a, QPoint b, bool manhattan = false)
{ return pointsOnLine(a.x(), a.y(), b.x(), b.y(), manhattan); }

Expand Down
2 changes: 2 additions & 0 deletions src/tiled/libtilededitor.qbs
Original file line number Diff line number Diff line change
Expand Up @@ -451,6 +451,8 @@ DynamicLibrary {
"scriptfileformatwrappers.h",
"scriptfileinfo.cpp",
"scriptfileinfo.h",
"scriptgeometry.cpp",
"scriptgeometry.h",
"scriptimage.cpp",
"scriptimage.h",
"scriptmanager.cpp",
Expand Down
100 changes: 100 additions & 0 deletions src/tiled/scriptgeometry.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
/*
* scriptgeometry.h
* Copyright 2023, Thorbjørn Lindeijer <bjorn@lindeijer.nl>
*
* This file is part of Tiled.
*
* 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 2 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 "scriptgeometry.h"

#include "geometry.h"
#include "regionvaluetype.h"

#include <QJSEngine>

namespace Tiled {

/**
* This class makes select geometry functions available to the scripting API.
*/
class ScriptGeometry : public QObject
{
Q_OBJECT

public:
explicit ScriptGeometry(QObject *parent = nullptr)
: QObject(parent)
{}

#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
Q_INVOKABLE QJSValue pointsOnLine(QPoint a, QPoint b, bool manhattan = false);
Q_INVOKABLE QJSValue pointsOnEllipse(QPoint center, int radiusX, int radiusY);
#else
Q_INVOKABLE QVector<QPoint> pointsOnLine(QPoint a, QPoint b, bool manhattan = false)
{ return Tiled::pointsOnLine(a, b, manhattan); }

Q_INVOKABLE QVector<QPoint> pointsOnEllipse(QPoint center, int radiusX, int radiusY)
{ return Tiled::pointsOnEllipse(center, radiusX, radiusY); }
#endif

Q_INVOKABLE Tiled::RegionValueType ellipseRegion(QRect rect)
{ return RegionValueType { Tiled::ellipseRegion(rect) }; }

Q_INVOKABLE Tiled::RegionValueType ellipseRegion(int x0, int y0, int x1, int y1)
{ return RegionValueType { Tiled::ellipseRegion(x0, y0, x1, y1) }; }
};

#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
static QJSValue toJSValue(const QVector<QPoint> &points, QJSEngine *engine)
{
if (!engine)
return QJSValue();

QJSValue array = engine->newArray(points.size());

for (int i = 0; i < points.size(); ++i) {
QJSValue pointObject = engine->newObject();
pointObject.setProperty(QStringLiteral("x"), points.at(i).x());
pointObject.setProperty(QStringLiteral("y"), points.at(i).y());
array.setProperty(i, pointObject);
}

return array;
}

QJSValue ScriptGeometry::pointsOnLine(QPoint a, QPoint b, bool manhattan)
{
return toJSValue(Tiled::pointsOnLine(a, b, manhattan),
qjsEngine(this));
}

QJSValue ScriptGeometry::pointsOnEllipse(QPoint center, int radiusX, int radiusY)
{
return toJSValue(Tiled::pointsOnEllipse(center, radiusX, radiusY),
qjsEngine(this));
}
#endif

void registerGeometry(QJSEngine *jsEngine)
{
jsEngine->globalObject().setProperty(QStringLiteral("Geometry"),
jsEngine->newQObject(new ScriptGeometry));

}

} // namespace Tiled

#include "scriptgeometry.moc"
29 changes: 29 additions & 0 deletions src/tiled/scriptgeometry.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* scriptgeometry.h
* Copyright 2023, Thorbjørn Lindeijer <bjorn@lindeijer.nl>
*
* This file is part of Tiled.
*
* 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 2 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/>.
*/

#pragma once

class QJSEngine;

namespace Tiled {

void registerGeometry(QJSEngine *jsEngine);

} // namespace Tiled
5 changes: 4 additions & 1 deletion src/tiled/scriptmanager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
*/

#include "scriptmanager.h"

#include "editablegrouplayer.h"
#include "editableimagelayer.h"
#include "editablemap.h"
Expand All @@ -37,15 +38,16 @@
#include "projectmanager.h"
#include "regionvaluetype.h"
#include "scriptbase64.h"
#include "scriptdialog.h"
#include "scriptedaction.h"
#include "scriptedtool.h"
#include "scriptfile.h"
#include "scriptfileformatwrappers.h"
#include "scriptfileinfo.h"
#include "scriptgeometry.h"
#include "scriptimage.h"
#include "scriptmodule.h"
#include "scriptprocess.h"
#include "scriptdialog.h"
#include "tilecollisiondock.h"
#include "tilelayer.h"
#include "tilelayeredit.h"
Expand Down Expand Up @@ -405,6 +407,7 @@ void ScriptManager::initialize()
registerDialog(engine);
registerFile(engine);
registerFileInfo(engine);
registerGeometry(engine);
registerProcess(engine);
loadExtensions();
}
Expand Down

0 comments on commit 4a0f569

Please sign in to comment.