Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Scripting: Added Geometry interface with line and ellipse helpers #3779

Merged
merged 6 commits into from
Aug 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
* Removed Space and Ctrl+Space shortcuts from Layers view to avoid conflict with panning (#3672)
* Scripting: Added API for editing tile layers using terrain sets (with a-morphous, #3758)
* Scripting: Support erasing tiles in Tool.preview and TileMap.merge
* Scripting: Added Geometry interface with line and ellipse helpers
* Scripting: Added WangSet.effectiveTypeForColor
* Fixed object preview position with parallax factor on group layer (#3669)
* Fixed hover highlight rendering with active parallax factor (#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 @@ -1494,6 +1494,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 <[email protected]>
*
* 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 <[email protected]>
*
* 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
Loading