├── .gitignore
├── LICENSE
├── NOTICE
├── README.markdown
├── build.gradle
├── editor
├── build.gradle
├── libs
│ ├── css-engine-swing-api-sources.jar
│ ├── css-engine-swing-api.jar
│ ├── tween-engine-api-sources.jar
│ └── tween-engine-api.jar
├── src
│ └── main
│ │ ├── java
│ │ └── aurelienribon
│ │ │ ├── Res.java
│ │ │ ├── accessors
│ │ │ └── SpriteAccessor.java
│ │ │ ├── bodyeditor
│ │ │ ├── Ctx.java
│ │ │ ├── DynamicObjectsManager.java
│ │ │ ├── IoManager.java
│ │ │ ├── RigidBodiesManager.java
│ │ │ ├── Settings.java
│ │ │ ├── canvas
│ │ │ │ ├── Assets.java
│ │ │ │ ├── Canvas.java
│ │ │ │ ├── CanvasDrawer.java
│ │ │ │ ├── InputHelper.java
│ │ │ │ ├── Label.java
│ │ │ │ ├── PanZoomInputProcessor.java
│ │ │ │ ├── dynamicobjects
│ │ │ │ │ ├── BodiesList.java
│ │ │ │ │ ├── BodiesListItem.java
│ │ │ │ │ └── DynamicObjectsScreen.java
│ │ │ │ └── rigidbodies
│ │ │ │ │ ├── RigidBodiesScreen.java
│ │ │ │ │ ├── RigidBodiesScreenDrawer.java
│ │ │ │ │ └── input
│ │ │ │ │ ├── CreationInputProcessor.java
│ │ │ │ │ ├── EditionInputProcessor.java
│ │ │ │ │ └── TestInputProcessor.java
│ │ │ ├── io
│ │ │ │ └── JsonIo.java
│ │ │ ├── maths
│ │ │ │ ├── Clipper.java
│ │ │ │ ├── Tracer.java
│ │ │ │ ├── earclipping
│ │ │ │ │ ├── bayazit
│ │ │ │ │ │ └── BayazitDecomposer.java
│ │ │ │ │ └── ewjordan
│ │ │ │ │ │ ├── EwjordanDecomposer.java
│ │ │ │ │ │ ├── Polygon.java
│ │ │ │ │ │ └── Triangle.java
│ │ │ │ └── trace
│ │ │ │ │ └── TextureConverter.java
│ │ │ ├── models
│ │ │ │ ├── CircleModel.java
│ │ │ │ ├── DynamicObjectModel.java
│ │ │ │ ├── PolygonModel.java
│ │ │ │ ├── RigidBodyModel.java
│ │ │ │ └── ShapeModel.java
│ │ │ ├── ui
│ │ │ │ ├── AutoTraceParamsDialog.form
│ │ │ │ ├── AutoTraceParamsDialog.java
│ │ │ │ ├── DynamicObjectsPanel.form
│ │ │ │ ├── DynamicObjectsPanel.java
│ │ │ │ ├── HelpDialog.form
│ │ │ │ ├── HelpDialog.java
│ │ │ │ ├── Main.java
│ │ │ │ ├── MainWindow.form
│ │ │ │ ├── MainWindow.java
│ │ │ │ ├── ProjectPanel.form
│ │ │ │ ├── ProjectPanel.java
│ │ │ │ ├── RepairImagePathsDialog.form
│ │ │ │ ├── RepairImagePathsDialog.java
│ │ │ │ ├── RigidBodiesCreationDialog.form
│ │ │ │ ├── RigidBodiesCreationDialog.java
│ │ │ │ ├── RigidBodiesOptionsPanel.form
│ │ │ │ ├── RigidBodiesOptionsPanel.java
│ │ │ │ ├── RigidBodiesPanel.form
│ │ │ │ └── RigidBodiesPanel.java
│ │ │ └── utils
│ │ │ │ └── ShapeUtils.java
│ │ │ └── utils
│ │ │ ├── gdx
│ │ │ ├── PolygonUtils.java
│ │ │ ├── SpriteUtils.java
│ │ │ ├── TextureUtils.java
│ │ │ └── VectorUtils.java
│ │ │ ├── io
│ │ │ ├── FilenameHelper.java
│ │ │ └── HttpUtils.java
│ │ │ ├── notifications
│ │ │ ├── AutoListModel.java
│ │ │ ├── ChangeListener.java
│ │ │ ├── Changeable.java
│ │ │ ├── ChangeableObject.java
│ │ │ ├── ChangeableSupport.java
│ │ │ └── ObservableList.java
│ │ │ └── ui
│ │ │ └── SwingHelper.java
│ │ └── resources
│ │ ├── css
│ │ └── style.css
│ │ ├── data
│ │ ├── ball.png
│ │ ├── transparent-dark.png
│ │ ├── transparent-light.png
│ │ ├── unknown.png
│ │ ├── v00.png
│ │ ├── v01.png
│ │ ├── v10.png
│ │ └── white.png
│ │ └── gfx
│ │ ├── autoTrace.png
│ │ ├── bg.png
│ │ ├── comingSoon.png
│ │ ├── ic_add.png
│ │ ├── ic_createShape.png
│ │ ├── ic_delete.png
│ │ ├── ic_down.png
│ │ ├── ic_edit.png
│ │ ├── ic_editShape.png
│ │ ├── ic_error.png
│ │ ├── ic_export.png
│ │ ├── ic_file.png
│ │ ├── ic_gear.png
│ │ ├── ic_help.png
│ │ ├── ic_import.png
│ │ ├── ic_loading.gif
│ │ ├── ic_lock.png
│ │ ├── ic_manual.png
│ │ ├── ic_new.png
│ │ ├── ic_ok.png
│ │ ├── ic_open.png
│ │ ├── ic_remove.png
│ │ ├── ic_save.png
│ │ ├── ic_shape.png
│ │ ├── ic_test.png
│ │ ├── ic_texture.png
│ │ ├── ic_unlock.png
│ │ ├── ic_up.png
│ │ ├── ic_warning.png
│ │ ├── ic_wrench.png
│ │ ├── newBody.png
│ │ ├── title.png
│ │ └── unknown.png
└── test-me!
│ ├── gfx
│ ├── test01.png
│ ├── test02 (non POT).png
│ ├── test03 (multi shapes).png
│ ├── test04 (non square).png
│ └── test05 (non square).png
│ └── test.json
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── loader-libgdx-demo
├── build.gradle
├── libs
│ ├── gdx-backend-lwjgl-natives.jar
│ ├── gdx-backend-lwjgl-sources.jar
│ ├── gdx-backend-lwjgl.jar
│ ├── gdx-natives.jar
│ ├── gdx-sources.jar
│ ├── gdx.jar
│ ├── tween-engine-api-sources.jar
│ └── tween-engine-api.jar
└── src
│ └── main
│ ├── java
│ └── aurelienribon
│ │ └── bodyeditor
│ │ ├── App.java
│ │ └── Main.java
│ └── resources
│ └── data
│ ├── gfx
│ ├── ball.png
│ ├── bottle.png
│ └── white.png
│ └── test.json
├── loader-libgdx
├── build.gradle
└── src
│ └── aurelienribon
│ ├── bodyeditor
│ └── BodyEditorLoader.java
│ └── bodyeditorloader.gwt.xml
├── readmeImgs
├── aurelienribon - box2d-editor is apache 2.png
├── pbe-02.jpg
├── pbe-04.jpg
├── pbe-autotrace.jpg
├── pbe-circle-shapes.jpg
├── pbe-loader-demo.jpg
├── pbe-reference-point.jpg
├── pbe-workflow.jpg
└── slide-physics-body-editor.jpg
└── settings.gradle
/.gitignore:
--------------------------------------------------------------------------------
1 | ## General
2 | hs_err_pid*
3 | .gradle
4 |
5 | ## Intellij
6 | .idea/
7 | *.ipr
8 | *.iws
9 | *.iml
10 | out/
11 | local.properties
12 |
13 | ## Eclipse
14 | .classpath
15 | .project
16 | .metadata
17 | **/bin/
18 | tmp/
19 | *.tmp
20 | *.bak
21 | *.swp
22 | *~.nib
23 |
24 | .settings/
25 | .loadpath
26 | .externalToolBuilders/
27 | *.launch
28 |
29 | ## NetBeans
30 | **/nbproject/private/
31 | build/
32 | nbbuild/
33 | dist/
34 | nbdist/
35 | nbactions.xml
36 | nb-configuration.xml
37 |
38 | ## OS Specific
39 | .DS_Store
40 | Icon
41 | ehthumbs.db
42 | Thumbs.db
43 |
44 | editor/manifest.mf
45 | editor/nbbuild.xml
46 | editor/build/*
47 | editor/dist/*
48 | editor/nbproject/*
49 | editor/bin/*
50 | editor/.classpath
51 | editor/.project
52 | editor/*.jar
53 |
54 | loader-libgdx/manifest.mf
55 | loader-libgdx/nbbuild.xml
56 | loader-libgdx/build/*
57 | loader-libgdx/dist/*
58 | loader-libgdx/nbproject/*
59 | loader-libgdx/bin/*
60 | loader-libgdx/.classpath
61 | loader-libgdx/.project
62 | loader-libgdx/*.jar
63 |
64 | loader-libgdx-demo/manifest.mf
65 | loader-libgdx-demo/nbbuild.xml
66 | loader-libgdx-demo/build/*
67 | loader-libgdx-demo/dist/*
68 | loader-libgdx-demo/nbproject/*
69 | loader-libgdx-demo/bin/*
70 | loader-libgdx-demo/.classpath
71 | loader-libgdx-demo/.project
72 | loader-libgdx-demo/*.jar
73 |
74 |
--------------------------------------------------------------------------------
/NOTICE:
--------------------------------------------------------------------------------
1 | Physics Body Editor (Also known as box2d-editor)
2 |
3 | This product includes software developed by
4 | aurelienribon - http://www.aurelienribon.com/blog/projects/physics-body-editor/
5 | MovingBlocks - http://terasology.org
6 |
7 | Licensing:
8 | Comment states code is Apache 2: http://www.aurelienribon.com/blog/projects/physics-body-editor/?replytocom=2632#respond
9 | Archived link of blog + comments: https://archive.is/YjOvx
10 | Screenshot of comment: https://github.com/PrivateAlpha/box2d-editor/blob/master/readmeImgs/aurelienribon%20-%20box2d-editor%20is%20apache%202.png
11 |
--------------------------------------------------------------------------------
/README.markdown:
--------------------------------------------------------------------------------
1 | Physics Body Editor
2 | ==========
3 |
4 |
5 | Introduction
6 | --------
7 |
8 | Physics Body Editor is all about making your life easier with physics engines. Specifically, it targets the creation of collision shapes for your game objects: we call them rigid bodies. It can also let you combine these objects together and link them with joints to create complex objects: we call them dynamic objects.
9 |
10 | The problem we want to solve is as follows: have a look at the image on the right, I wanted to create a bottle that can hold objects inside it. At first, I used a drawing tool to draw my shape points over the bottle image, and I reported the values in my game. For each point, I had to convert from pixel units to world units of course. Boring. Oh, and guess what? It didn’t work! Indeed, physics engines usually only work with convex polygons! On to decompose the shape into multiple convex polygons by hand… More than boring. And of course, each time I wanted to do a little change, I had to go over the same process.
11 |
12 | I guess you understand why such automated tool can be handy: it converts pixel units to world units, decomposes the shape into multiple convex polygons, and lets you test the result directly!
13 | Features
14 |
15 | * Automatically decomposes concave shapes into convex polygons,
16 | * Automatically traces your images if needed,
17 | * Supports multiple outlines for a single body,
18 | * Supports polygon and circle shapes,
19 | * Reference point location can be changed,
20 | * Visual configurable grid with snap-to-grid option,
21 | * Built-in collision tester! Throw balls at your body to test it,
22 | * Loader provided for LibGDX game framework,
23 | * Simple export format (JSON), to let you create your own loader for any framework in any language.
24 |
25 |
26 |
27 |
28 | Technologies
29 | --------
30 |
31 | The application uses the following technologies:
32 |
33 | * [LibGDX](https://github.com/libgdx/libgdx), the most awesome game dev library, for the rendering of the canvas area,
34 | * [Box2d](http://box2d.org/), as the embedded physics engine (available in Java thanks to libGDX),
35 | * [Farseer engine](http://farseerphysics.codeplex.com/), for its auto-trace and polygon decomposition algorithms.
36 |
37 | Getting Started
38 | --------
39 | * [Wiki](https://github.com/MovingBlocks/box2d-editor/wiki)
40 | * [YouTube](https://youtu.be/KASY91EiTXQ)
41 |
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | repositories {
3 | mavenCentral()
4 | }
5 | }
6 |
7 | allprojects {
8 | apply plugin: "eclipse"
9 | apply plugin: "idea"
10 |
11 | version = '1.3.1'
12 | ext {
13 | appName = 'Physics Body Editor'
14 | gdxVersion = '1.12.1'
15 | }
16 |
17 | repositories {
18 | // Good ole Maven central
19 | mavenCentral()
20 |
21 | // Repos for LibGDX
22 | maven { url "https://oss.sonatype.org/content/repositories/snapshots/" }
23 | maven { url "https://oss.sonatype.org/content/repositories/releases/" }
24 |
25 | // Terasology Artifactory for any shared libs
26 | //maven { url "http://artifactory.terasology.org/artifactory/virtual-repo-live" }
27 | }
28 | }
29 |
30 | tasks.eclipse.doLast {
31 | delete ".project"
32 | }
33 | cleanIdea.doLast {
34 | new File('box2d-editor.iws').delete()
35 | }
36 |
--------------------------------------------------------------------------------
/editor/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'application'
3 | }
4 |
5 | sourceCompatibility = 1.8
6 | [compileJava, compileTestJava]*.options*.encoding = 'UTF-8'
7 | project.ext.mainClassName = "aurelienribon.bodyeditor.ui.Main"
8 | dependencies {
9 | implementation "com.badlogicgames.gdx:gdx:$gdxVersion"
10 | implementation "com.badlogicgames.gdx:gdx-box2d:$gdxVersion"
11 | implementation "com.badlogicgames.gdx:gdx-backend-lwjgl:$gdxVersion"
12 | implementation "com.badlogicgames.gdx:gdx-box2d-platform:$gdxVersion:natives-desktop"
13 | implementation "com.badlogicgames.gdx:gdx-platform:$gdxVersion"
14 | implementation "com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-desktop"
15 |
16 | implementation "net.java.balloontip:balloontip:1.2.1"
17 | implementation "commons-io:commons-io:2.1"
18 | implementation "org.json:json:20090211"
19 |
20 | implementation files('./libs/css-engine-swing-api.jar')
21 | implementation files('./libs/tween-engine-api.jar')
22 |
23 | }
24 |
25 | sourceSets {
26 | main {
27 | java
28 | {
29 | srcDirs = ["src/main/java"]
30 | destinationDirectory.set(new File("$buildDir/classes"))
31 | }
32 | resources.srcDirs = ["src/main/resources"]
33 | output.resourcesDir 'build/classes'
34 | }
35 | }
36 |
37 | application {
38 | mainClass = 'aurelienribon.bodyeditor.ui.Main'
39 | }
40 |
41 | jar {
42 | archiveFileName = "PhysicsBodyEditor.jar"
43 |
44 | // doFirst {
45 | // copy {
46 | // from 'src/SolAppListener.gwt.xml'
47 | // into 'build/classes/main'
48 | // }
49 | // }
50 | }
51 |
52 |
53 | distributions {
54 | main {
55 | contents {
56 | from('test-me!') {
57 | into 'test-me!'
58 | }
59 | }
60 | }
61 | }
62 |
63 | eclipse.project {
64 | name = appName + "-editor"
65 | }
66 |
67 | idea {
68 | module {
69 | // Change around the output a bit
70 | inheritOutputDirs = false
71 | outputDir = file('build/classes')
72 | testOutputDir = file('build/testClasses')
73 | downloadSources = true
74 | }
75 | }
--------------------------------------------------------------------------------
/editor/libs/css-engine-swing-api-sources.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MovingBlocks/box2d-editor/05c7ea15b80fff859c6abc91c1761979cbfd31a3/editor/libs/css-engine-swing-api-sources.jar
--------------------------------------------------------------------------------
/editor/libs/css-engine-swing-api.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MovingBlocks/box2d-editor/05c7ea15b80fff859c6abc91c1761979cbfd31a3/editor/libs/css-engine-swing-api.jar
--------------------------------------------------------------------------------
/editor/libs/tween-engine-api-sources.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MovingBlocks/box2d-editor/05c7ea15b80fff859c6abc91c1761979cbfd31a3/editor/libs/tween-engine-api-sources.jar
--------------------------------------------------------------------------------
/editor/libs/tween-engine-api.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MovingBlocks/box2d-editor/05c7ea15b80fff859c6abc91c1761979cbfd31a3/editor/libs/tween-engine-api.jar
--------------------------------------------------------------------------------
/editor/src/main/java/aurelienribon/Res.java:
--------------------------------------------------------------------------------
1 | package aurelienribon;
2 |
3 | import javax.swing.*;
4 | import java.io.InputStream;
5 | import java.net.URL;
6 | import java.util.HashMap;
7 | import java.util.Map;
8 |
9 | /**
10 | * @author Aurelien Ribon | http://www.aurelienribon.com/
11 | */
12 | public class Res {
13 | private static Map imageIcons = new HashMap();
14 |
15 | public static ImageIcon getImage(String name) {
16 | if (!imageIcons.containsKey(name)) {
17 | URL url = Res.class.getResource(name);
18 | if (url == null) throw new RuntimeException("File not found: " + name);
19 | imageIcons.put(name, new ImageIcon(url));
20 | }
21 |
22 | return imageIcons.get(name);
23 | }
24 |
25 | public static InputStream getStream(String name) {
26 | InputStream is = Res.class.getResourceAsStream(name);
27 | if (is == null) throw new RuntimeException("File not found: " + name);
28 | return is;
29 | }
30 |
31 | public static URL getUrl(String name) {
32 | URL url = Res.class.getResource(name);
33 | if (url == null) throw new RuntimeException("File not found: " + name);
34 | return url;
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/editor/src/main/java/aurelienribon/accessors/SpriteAccessor.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2017 MovingBlocks
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package aurelienribon.accessors;
17 |
18 | import aurelienribon.tweenengine.TweenAccessor;
19 | import com.badlogic.gdx.graphics.Color;
20 | import com.badlogic.gdx.graphics.g2d.Sprite;
21 |
22 | public class SpriteAccessor implements TweenAccessor {
23 | public static final int POS_XY = 1;
24 | public static final int POS_X = 10;
25 | public static final int POS_Y = 11;
26 | public static final int CPOS_XY = 2;
27 | public static final int SCALE_XY = 3;
28 | public static final int ROTATION = 4;
29 | public static final int OPACITY = 5;
30 | public static final int TINT = 6;
31 |
32 | @Override
33 | public int getValues(Sprite target, int tweenType, float[] returnValues) {
34 | switch (tweenType) {
35 | case POS_XY:
36 | returnValues[0] = target.getX();
37 | returnValues[1] = target.getY();
38 | return 2;
39 |
40 | case POS_X:
41 | returnValues[0] = target.getX();
42 | return 1;
43 |
44 | case POS_Y:
45 | returnValues[0] = target.getY();
46 | return 1;
47 |
48 | case CPOS_XY:
49 | returnValues[0] = target.getX() + target.getWidth() / 2;
50 | returnValues[1] = target.getY() + target.getHeight() / 2;
51 | return 2;
52 |
53 | case SCALE_XY:
54 | returnValues[0] = target.getScaleX();
55 | returnValues[1] = target.getScaleY();
56 | return 2;
57 |
58 | case ROTATION:
59 | returnValues[0] = target.getRotation();
60 | return 1;
61 |
62 | case OPACITY:
63 | returnValues[0] = target.getColor().a;
64 | return 1;
65 |
66 | case TINT:
67 | returnValues[0] = target.getColor().r;
68 | returnValues[1] = target.getColor().g;
69 | returnValues[2] = target.getColor().b;
70 | return 3;
71 |
72 | default:
73 | assert false;
74 | return -1;
75 | }
76 | }
77 |
78 | @Override
79 | public void setValues(Sprite target, int tweenType, float[] newValues) {
80 | switch (tweenType) {
81 | case POS_XY:
82 | target.setPosition(newValues[0], newValues[1]);
83 | break;
84 |
85 | case POS_X:
86 | target.setPosition(newValues[0], target.getY());
87 | break;
88 |
89 | case POS_Y:
90 | target.setPosition(target.getX(), newValues[0]);
91 | break;
92 |
93 | case CPOS_XY:
94 | target.setPosition(newValues[0] - target.getWidth() / 2, newValues[1] - target.getHeight() / 2);
95 | break;
96 |
97 | case SCALE_XY:
98 | target.setScale(newValues[0], newValues[1]);
99 | break;
100 |
101 | case ROTATION:
102 | target.setRotation(newValues[0]);
103 | break;
104 |
105 | case OPACITY:
106 | Color c = target.getColor();
107 | c.set(c.r, c.g, c.b, newValues[0]);
108 | target.setColor(c);
109 | break;
110 |
111 | case TINT:
112 | c = target.getColor();
113 | c.set(newValues[0], newValues[1], newValues[2], c.a);
114 | target.setColor(c);
115 | break;
116 |
117 | default:
118 | assert false;
119 | }
120 | }
121 | }
122 |
--------------------------------------------------------------------------------
/editor/src/main/java/aurelienribon/bodyeditor/Ctx.java:
--------------------------------------------------------------------------------
1 | package aurelienribon.bodyeditor;
2 |
3 | import aurelienribon.bodyeditor.ui.MainWindow;
4 |
5 | /**
6 | * @author Aurelien Ribon | http://www.aurelienribon.com/
7 | */
8 | public class Ctx {
9 | public static final IoManager io = new IoManager();
10 | public static final RigidBodiesManager bodies = new RigidBodiesManager();
11 | public static final DynamicObjectsManager objects = new DynamicObjectsManager();
12 | public static final MainWindow window = new MainWindow();
13 | }
14 |
--------------------------------------------------------------------------------
/editor/src/main/java/aurelienribon/bodyeditor/DynamicObjectsManager.java:
--------------------------------------------------------------------------------
1 | package aurelienribon.bodyeditor;
2 |
3 | import aurelienribon.bodyeditor.models.DynamicObjectModel;
4 | import aurelienribon.utils.notifications.ChangeableObject;
5 | import aurelienribon.utils.notifications.ObservableList;
6 |
7 | import java.util.List;
8 |
9 | /**
10 | * @author Aurelien Ribon | http://www.aurelienribon.com/
11 | */
12 | public class DynamicObjectsManager extends ChangeableObject {
13 | public static final String PROP_SELECTION = "selection";
14 |
15 | private final ObservableList models = new ObservableList(this);
16 | private DynamicObjectModel selectedModel = null;
17 |
18 | public DynamicObjectsManager() {
19 | models.addListChangedListener(new ObservableList.ListChangeListener() {
20 | @Override
21 | public void changed(Object source, List added, List removed) {
22 | if (!models.contains(selectedModel)) select(null);
23 | }
24 | });
25 | }
26 |
27 | public ObservableList getModels() {
28 | return models;
29 | }
30 |
31 | public DynamicObjectModel getSelectedModel() {
32 | assert selectedModel == null || models.contains(selectedModel);
33 | return selectedModel;
34 | }
35 |
36 | public void select(DynamicObjectModel model) {
37 | assert model == null || models.contains(model);
38 | selectedModel = model;
39 | firePropertyChanged(PROP_SELECTION);
40 | }
41 |
42 | public DynamicObjectModel getModel(String name) {
43 | for (DynamicObjectModel model : models)
44 | if (model.getName().equals(name)) return model;
45 | return null;
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/editor/src/main/java/aurelienribon/bodyeditor/IoManager.java:
--------------------------------------------------------------------------------
1 | package aurelienribon.bodyeditor;
2 |
3 | import aurelienribon.bodyeditor.io.JsonIo;
4 | import aurelienribon.utils.io.FilenameHelper;
5 | import aurelienribon.utils.notifications.ChangeableObject;
6 | import org.apache.commons.io.FileUtils;
7 | import org.json.JSONException;
8 |
9 | import java.io.File;
10 | import java.io.IOException;
11 |
12 | /**
13 | * @author Aurelien Ribon | http://www.aurelienribon.com/
14 | */
15 | public class IoManager extends ChangeableObject {
16 | public static final String PROP_PROJECTFILE = "projectFile";
17 | private File projectFile;
18 |
19 | public File getProjectFile() {
20 | return projectFile;
21 | }
22 |
23 | public File getProjectDir() {
24 | return projectFile.getParentFile();
25 | }
26 |
27 | public void setProjectFile(File projectFile) {
28 | this.projectFile = projectFile;
29 | firePropertyChanged(PROP_PROJECTFILE);
30 | }
31 |
32 | public void exportToFile() throws IOException, JSONException {
33 | assert projectFile != null;
34 |
35 | String str = JsonIo.serialize();
36 | FileUtils.writeStringToFile(projectFile, str);
37 | }
38 |
39 | public void importFromFile() throws IOException, JSONException {
40 | assert projectFile != null;
41 | assert projectFile.isFile();
42 |
43 | Ctx.bodies.getModels().clear();
44 | String str = FileUtils.readFileToString(projectFile);
45 |
46 | JsonIo.deserialize(str);
47 | }
48 |
49 | public String buildImagePath(File imgFile) {
50 | return FilenameHelper.getRelativePath(imgFile.getPath(), projectFile.getParent());
51 | }
52 |
53 | public File getImageFile(String imgPath) {
54 | if (imgPath == null) return null;
55 | File file = new File(projectFile.getParent(), imgPath);
56 | return file;
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/editor/src/main/java/aurelienribon/bodyeditor/RigidBodiesManager.java:
--------------------------------------------------------------------------------
1 | package aurelienribon.bodyeditor;
2 |
3 | import aurelienribon.bodyeditor.models.RigidBodyModel;
4 | import aurelienribon.utils.notifications.ChangeableObject;
5 | import aurelienribon.utils.notifications.ObservableList;
6 |
7 | import java.util.List;
8 |
9 | /**
10 | * @author Aurelien Ribon | http://www.aurelienribon.com/
11 | */
12 | public class RigidBodiesManager extends ChangeableObject {
13 | public static final String PROP_SELECTION = "selection";
14 |
15 | private final ObservableList models = new ObservableList(this);
16 | private RigidBodyModel selectedModel;
17 |
18 | public RigidBodiesManager() {
19 | models.addListChangedListener(new ObservableList.ListChangeListener() {
20 | @Override
21 | public void changed(Object source, List added, List removed) {
22 | if (!models.contains(selectedModel)) select(null);
23 | }
24 | });
25 | }
26 |
27 | public ObservableList getModels() {
28 | return models;
29 | }
30 |
31 | public RigidBodyModel getSelectedModel() {
32 | assert selectedModel == null || models.contains(selectedModel);
33 | return selectedModel;
34 | }
35 |
36 | public void select(RigidBodyModel model) {
37 | assert model == null || models.contains(model);
38 | selectedModel = model;
39 | firePropertyChanged(PROP_SELECTION);
40 | }
41 |
42 | public RigidBodyModel getModel(String name) {
43 | for (RigidBodyModel model : models)
44 | if (model.getName().equals(name)) return model;
45 | return null;
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/editor/src/main/java/aurelienribon/bodyeditor/Settings.java:
--------------------------------------------------------------------------------
1 | package aurelienribon.bodyeditor;
2 |
3 | import aurelienribon.bodyeditor.maths.Clipper.Polygonizer;
4 |
5 | /**
6 | * @author Aurelien Ribon | http://www.aurelienribon.com/
7 | */
8 | public class Settings {
9 | public static boolean isImageDrawn = true;
10 | public static boolean isShapeDrawn = true;
11 | public static boolean isPolygonDrawn = true;
12 | public static boolean isPhysicsDebugEnabled = false;
13 | public static boolean isSnapToGridEnabled = false;
14 | public static boolean isAxisShown = true;
15 | public static boolean isGridShown = false;
16 | public static float gridGap = 0.03f;
17 | public static Polygonizer polygonizer = Polygonizer.BAYAZIT;
18 | public static float autoTraceHullTolerance = 2.5f;
19 | public static int autoTraceAlphaTolerance = 128;
20 | public static boolean autoTraceMultiPartDetection = false;
21 | public static boolean autoTraceHoleDetection = false;
22 | }
23 |
--------------------------------------------------------------------------------
/editor/src/main/java/aurelienribon/bodyeditor/canvas/Assets.java:
--------------------------------------------------------------------------------
1 | package aurelienribon.bodyeditor.canvas;
2 |
3 | import aurelienribon.bodyeditor.Ctx;
4 | import aurelienribon.bodyeditor.models.RigidBodyModel;
5 | import aurelienribon.utils.gdx.TextureUtils;
6 | import aurelienribon.utils.notifications.ObservableList;
7 | import com.badlogic.gdx.assets.AssetManager;
8 | import com.badlogic.gdx.graphics.Texture;
9 | import com.badlogic.gdx.graphics.g2d.TextureRegion;
10 |
11 | import java.io.File;
12 | import java.util.HashMap;
13 | import java.util.List;
14 | import java.util.Map;
15 |
16 | /**
17 | * @author Aurelien Ribon | http://www.aurelienribon.com/
18 | */
19 | public class Assets extends AssetManager {
20 | private static Assets instance = new Assets();
21 |
22 | public static Assets inst() {
23 | return instance;
24 | }
25 |
26 | private final Map rigidBodiesRegions = new HashMap();
27 | private TextureRegion unknownRegion;
28 |
29 | public void initialize() {
30 | String[] texturesNearest = new String[]{
31 | "data/transparent-light.png",
32 | "data/transparent-dark.png",
33 | "data/white.png"
34 | };
35 |
36 | String[] texturesLinear = new String[]{
37 | "data/ball.png",
38 | "data/v00.png",
39 | "data/v01.png",
40 | "data/v10.png",
41 | "data/unknown.png"
42 | };
43 |
44 | for (String tex : texturesNearest) load(tex, Texture.class);
45 | for (String tex : texturesLinear) load(tex, Texture.class);
46 |
47 | while (update() == false) {
48 | }
49 |
50 | for (String tex : texturesLinear) {
51 | get(tex, Texture.class).setFilter(Texture.TextureFilter.Linear, Texture.TextureFilter.Linear);
52 | }
53 |
54 | unknownRegion = new TextureRegion(get("data/unknown.png", Texture.class));
55 |
56 | Ctx.bodies.getModels().addListChangedListener(new ObservableList.ListChangeListener() {
57 | @Override
58 | public void changed(Object source, List added, List removed) {
59 | for (RigidBodyModel body : removed) {
60 | TextureRegion region = rigidBodiesRegions.remove(body);
61 | if (region != null) region.getTexture().dispose();
62 | }
63 |
64 | for (RigidBodyModel body : added) {
65 | load(body);
66 | }
67 | }
68 | });
69 | }
70 |
71 | public TextureRegion getRegion(RigidBodyModel body) {
72 | if (!body.isImagePathValid()) return unknownRegion;
73 | if (body.getImagePath() == null) return null;
74 | if (!rigidBodiesRegions.containsKey(body)) load(body);
75 | return rigidBodiesRegions.get(body);
76 | }
77 |
78 | private void load(RigidBodyModel body) {
79 | if (!body.isImagePathValid()) return;
80 | if (body.getImagePath() == null) return;
81 |
82 | File file = Ctx.io.getImageFile(body.getImagePath());
83 | TextureRegion region = TextureUtils.getPOTTexture(file.getPath());
84 | rigidBodiesRegions.put(body, region);
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/editor/src/main/java/aurelienribon/bodyeditor/canvas/Canvas.java:
--------------------------------------------------------------------------------
1 | package aurelienribon.bodyeditor.canvas;
2 |
3 | import aurelienribon.accessors.SpriteAccessor;
4 | import aurelienribon.bodyeditor.Ctx;
5 | import aurelienribon.bodyeditor.RigidBodiesManager;
6 | import aurelienribon.bodyeditor.Settings;
7 | import aurelienribon.bodyeditor.canvas.dynamicobjects.DynamicObjectsScreen;
8 | import aurelienribon.bodyeditor.canvas.rigidbodies.RigidBodiesScreen;
9 | import aurelienribon.tweenengine.Tween;
10 | import aurelienribon.utils.notifications.ChangeListener;
11 | import com.badlogic.gdx.ApplicationAdapter;
12 | import com.badlogic.gdx.Gdx;
13 | import com.badlogic.gdx.InputMultiplexer;
14 | import com.badlogic.gdx.graphics.Color;
15 | import com.badlogic.gdx.graphics.OrthographicCamera;
16 | import com.badlogic.gdx.graphics.Texture;
17 | import com.badlogic.gdx.graphics.g2d.BitmapFont;
18 | import com.badlogic.gdx.graphics.g2d.Sprite;
19 | import com.badlogic.gdx.graphics.g2d.SpriteBatch;
20 | import com.badlogic.gdx.math.Vector2;
21 | import com.badlogic.gdx.math.Vector3;
22 |
23 | import java.util.List;
24 | import java.util.Locale;
25 | import java.util.concurrent.CopyOnWriteArrayList;
26 |
27 | /**
28 | * @author Aurelien Ribon | http://www.aurelienribon.com/
29 | */
30 | public class Canvas extends ApplicationAdapter {
31 | public OrthographicCamera worldCamera;
32 | public OrthographicCamera screenCamera;
33 | public SpriteBatch batch;
34 | public BitmapFont font;
35 | public CanvasDrawer drawer;
36 | public InputMultiplexer input;
37 |
38 | public enum Mode {BODIES, OBJECTS}
39 |
40 | private Mode mode = Mode.BODIES;
41 |
42 | private RigidBodiesScreen rigidBodiesScreen;
43 | private DynamicObjectsScreen dynamicObjectsScreen;
44 |
45 | private Sprite infoLabel;
46 | private Texture backgroundTexture;
47 |
48 | @Override
49 | public void create() {
50 | Assets.inst().initialize();
51 | Tween.registerAccessor(Sprite.class, new SpriteAccessor());
52 |
53 | worldCamera = new OrthographicCamera();
54 | screenCamera = new OrthographicCamera();
55 | resetCameras();
56 |
57 | batch = new SpriteBatch();
58 | font = new BitmapFont();
59 | drawer = new CanvasDrawer(batch, worldCamera);
60 |
61 | backgroundTexture = Assets.inst().get("data/transparent-light.png", Texture.class);
62 | backgroundTexture.setWrap(Texture.TextureWrap.Repeat, Texture.TextureWrap.Repeat);
63 |
64 | infoLabel = new Sprite(Assets.inst().get("data/white.png", Texture.class));
65 | infoLabel.setPosition(0, 0);
66 | infoLabel.setSize(120, 60);
67 | infoLabel.setColor(new Color(0x2A / 255f, 0x3B / 255f, 0x56 / 255f, 180 / 255f));
68 |
69 | input = new InputMultiplexer();
70 | input.addProcessor(new PanZoomInputProcessor(this));
71 | Gdx.input.setInputProcessor(input);
72 |
73 | rigidBodiesScreen = new RigidBodiesScreen(this);
74 | dynamicObjectsScreen = new DynamicObjectsScreen(this);
75 |
76 | initializeSelectionListeners();
77 | }
78 |
79 | // -------------------------------------------------------------------------
80 | // Init
81 | // -------------------------------------------------------------------------
82 |
83 | private void initializeSelectionListeners() {
84 | Ctx.bodies.addChangeListener(new ChangeListener() {
85 | @Override
86 | public void propertyChanged(Object source, String propertyName) {
87 | if (propertyName.equals(RigidBodiesManager.PROP_SELECTION)) {
88 | if (Ctx.bodies.getSelectedModel() != null) {
89 | Mode oldMode = mode;
90 | mode = Mode.BODIES;
91 | if (mode != oldMode) fireModeChanged(mode);
92 | }
93 | }
94 | }
95 | });
96 |
97 | Ctx.objects.addChangeListener(new ChangeListener() {
98 | @Override
99 | public void propertyChanged(Object source, String propertyName) {
100 | if (propertyName.equals(RigidBodiesManager.PROP_SELECTION)) {
101 | if (Ctx.objects.getSelectedModel() != null) {
102 | Mode oldMode = mode;
103 | mode = Mode.OBJECTS;
104 | if (mode != oldMode) fireModeChanged(mode);
105 | }
106 | }
107 | }
108 | });
109 | }
110 |
111 | // -------------------------------------------------------------------------
112 | // Render
113 | // -------------------------------------------------------------------------
114 |
115 | @Override
116 | public void render() {
117 | float w = Gdx.graphics.getWidth();
118 | float h = Gdx.graphics.getHeight();
119 |
120 | Gdx.gl30.glClearColor(1, 1, 1, 1);
121 | Gdx.gl30.glClear(Gdx.gl30.GL_COLOR_BUFFER_BIT);
122 |
123 | batch.setProjectionMatrix(screenCamera.combined);
124 | batch.begin();
125 | batch.disableBlending();
126 | float tw = backgroundTexture.getWidth();
127 | float th = backgroundTexture.getHeight();
128 | batch.draw(backgroundTexture, 0f, 0f, w, h, 0f, 0f, w / tw, h / th);
129 | batch.enableBlending();
130 | batch.end();
131 |
132 | rigidBodiesScreen.render();
133 | dynamicObjectsScreen.render();
134 |
135 | batch.setProjectionMatrix(screenCamera.combined);
136 | batch.begin();
137 | infoLabel.draw(batch);
138 | font.setColor(Color.WHITE);
139 | font.draw(batch, String.format(Locale.US, "Zoom: %.0f %%", 100f / worldCamera.zoom), 10, 45);
140 | font.draw(batch, "Fps: " + Gdx.graphics.getFramesPerSecond(), 10, 25);
141 | batch.end();
142 | }
143 |
144 | @Override
145 | public void resize(int width, int height) {
146 | Gdx.gl30.glViewport(0, 0, width, height);
147 | resetCameras();
148 | }
149 |
150 | // -------------------------------------------------------------------------
151 | // Public API
152 | // -------------------------------------------------------------------------
153 |
154 | public Vector2 screenToWorld(int x, int y) {
155 | Vector3 v3 = new Vector3(x, y, 0);
156 | worldCamera.unproject(v3);
157 | return new Vector2(v3.x, v3.y);
158 | }
159 |
160 | public Vector2 alignedScreenToWorld(int x, int y) {
161 | Vector2 p = screenToWorld(x, y);
162 | if (Settings.isSnapToGridEnabled) {
163 | float gap = Settings.gridGap;
164 | p.x = Math.round(p.x / gap) * gap;
165 | p.y = Math.round(p.y / gap) * gap;
166 | }
167 | return p;
168 | }
169 |
170 | // -------------------------------------------------------------------------
171 | // Events
172 | // -------------------------------------------------------------------------
173 |
174 | private final List listeners = new CopyOnWriteArrayList();
175 |
176 | public static interface Listener {
177 | public void modeChanged(Mode mode);
178 | }
179 |
180 | public void addListener(Listener listener) {
181 | listeners.add(listener);
182 | }
183 |
184 | private void fireModeChanged(Mode mode) {
185 | for (Listener listener : listeners) listener.modeChanged(mode);
186 | }
187 |
188 | // -------------------------------------------------------------------------
189 | // Internals
190 | // -------------------------------------------------------------------------
191 |
192 | private void resetCameras() {
193 | float w = Gdx.graphics.getWidth();
194 | float h = Gdx.graphics.getHeight();
195 |
196 | worldCamera.viewportWidth = w / 400;
197 | worldCamera.viewportHeight = w / 400 * h / w;
198 | worldCamera.position.set(0.5f, 0.5f, 0);
199 | worldCamera.update();
200 |
201 | screenCamera.viewportWidth = w;
202 | screenCamera.viewportHeight = h;
203 | screenCamera.position.set(w / 2, h / 2, 0);
204 | screenCamera.update();
205 | }
206 | }
207 |
--------------------------------------------------------------------------------
/editor/src/main/java/aurelienribon/bodyeditor/canvas/InputHelper.java:
--------------------------------------------------------------------------------
1 | package aurelienribon.bodyeditor.canvas;
2 |
3 | import com.badlogic.gdx.Gdx;
4 | import com.badlogic.gdx.Input.Keys;
5 |
6 | /**
7 | * @author Aurelien Ribon | http://www.aurelienribon.com/
8 | */
9 | public class InputHelper {
10 | public static boolean isCtrlDown() {
11 | return Gdx.input.isKeyPressed(Keys.CONTROL_LEFT)
12 | || Gdx.input.isKeyPressed(Keys.CONTROL_RIGHT)
13 | || Gdx.input.isKeyPressed(Keys.C);
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/editor/src/main/java/aurelienribon/bodyeditor/canvas/Label.java:
--------------------------------------------------------------------------------
1 | package aurelienribon.bodyeditor.canvas;
2 |
3 | import aurelienribon.tweenengine.Tween;
4 | import aurelienribon.tweenengine.TweenAccessor;
5 | import aurelienribon.tweenengine.TweenManager;
6 | import aurelienribon.utils.gdx.SpriteUtils;
7 | import com.badlogic.gdx.Gdx;
8 | import com.badlogic.gdx.graphics.Color;
9 | import com.badlogic.gdx.graphics.Texture;
10 | import com.badlogic.gdx.graphics.g2d.BitmapFont;
11 | import com.badlogic.gdx.graphics.g2d.GlyphLayout;
12 | import com.badlogic.gdx.graphics.g2d.Sprite;
13 | import com.badlogic.gdx.graphics.g2d.SpriteBatch;
14 |
15 | /**
16 | * @author Aurelien Ribon | http://www.aurelienribon.com/
17 | */
18 | public class Label {
19 | static {
20 | Tween.registerAccessor(Label.class, new Accessor());
21 | }
22 |
23 | public static enum Anchor {TOP_LEFT, BOTTOM_LEFT, TOP_RIGHT, BOTTOM_RIGHT}
24 |
25 | private static enum State {SHOWN, HIDDEN, HIDDEN_SEMI}
26 |
27 | private String text;
28 | private Sprite icon;
29 | private TouchCallback callback;
30 | private final BitmapFont font;
31 | private final Color color;
32 | private final Anchor anchor;
33 | private GlyphLayout layout = new GlyphLayout();
34 |
35 | private final TweenManager tweenManager = new TweenManager();
36 | private final Sprite bg;
37 | private final float y, w, h;
38 | private float offsetX;
39 | private boolean isTouchOver = false;
40 | private State state = State.HIDDEN;
41 |
42 | public Label(float y, float w, float h, String text, BitmapFont font, Color color, Anchor anchor) {
43 | this.y = y;
44 | this.w = w;
45 | this.h = h;
46 | this.text = text;
47 | this.font = font;
48 | this.color = color;
49 | this.anchor = anchor;
50 |
51 | this.bg = new Sprite(Assets.inst().get("data/white.png", Texture.class));
52 | bg.setSize(w * 11 / 10, h);
53 | bg.setColor(color);
54 |
55 | offsetX = -w;
56 | }
57 |
58 | // -------------------------------------------------------------------------
59 | // Callback
60 | // -------------------------------------------------------------------------
61 |
62 | public static interface TouchCallback {
63 | public void touchDown(Label source);
64 |
65 | public void touchEnter(Label source);
66 |
67 | public void touchExit(Label source);
68 | }
69 |
70 | // -------------------------------------------------------------------------
71 | // Public API
72 | // -------------------------------------------------------------------------
73 |
74 | public void setText(String text) {
75 | this.text = text;
76 | }
77 |
78 | public void setIcon(String path) {
79 | this.icon = new Sprite(Assets.inst().get(path, Texture.class));
80 | }
81 |
82 | public void setCallback(TouchCallback callback) {
83 | this.callback = callback;
84 | }
85 |
86 | public void hide() {
87 | if (state == State.HIDDEN) return;
88 | tweenManager.killTarget(this);
89 | Tween.to(this, Accessor.OFFSET_X, 0.3f).target(-w).start(tweenManager);
90 | Tween.to(this, Accessor.ALPHA, 0.3f).target(color.a).start(tweenManager);
91 | isTouchOver = false;
92 | state = State.HIDDEN;
93 | }
94 |
95 | public void hideSemi() {
96 | if (state == State.HIDDEN_SEMI) return;
97 | tweenManager.killTarget(this);
98 | Tween.to(this, Accessor.OFFSET_X, 0.3f).target(w / 10 - w).start(tweenManager);
99 | Tween.to(this, Accessor.ALPHA, 0.3f).target(color.a).start(tweenManager);
100 | isTouchOver = false;
101 | state = State.HIDDEN_SEMI;
102 | }
103 |
104 | public void show() {
105 | if (state == State.SHOWN) return;
106 | tweenManager.killTarget(this);
107 | Tween.to(this, Accessor.OFFSET_X, 0.3f).target(0).start(tweenManager);
108 | Tween.to(this, Accessor.ALPHA, 0.3f).target(color.a).start(tweenManager);
109 | state = State.SHOWN;
110 | }
111 |
112 | public void tiltOn() {
113 | float tx;
114 |
115 | switch (state) {
116 | case SHOWN:
117 | tx = w / 10;
118 | break;
119 | case HIDDEN_SEMI:
120 | tx = -w + w / 10 + w / 10;
121 | break;
122 | default:
123 | return;
124 | }
125 |
126 | tweenManager.killTarget(this);
127 | Tween.to(this, Accessor.ALPHA, 0.2f).target(1).start(tweenManager);
128 | Tween.to(this, Accessor.OFFSET_X, 0.2f).target(tx).start(tweenManager);
129 | }
130 |
131 | public void tiltOff() {
132 | float tx;
133 |
134 | switch (state) {
135 | case SHOWN:
136 | tx = 0;
137 | break;
138 | case HIDDEN_SEMI:
139 | tx = -w + w / 10;
140 | break;
141 | default:
142 | return;
143 | }
144 |
145 | tweenManager.killTarget(this);
146 | Tween.to(this, Accessor.ALPHA, 0.2f).target(color.a).start(tweenManager);
147 | Tween.to(this, Accessor.OFFSET_X, 0.2f).target(tx).start(tweenManager);
148 | }
149 |
150 | public void draw(SpriteBatch batch) {
151 | tweenManager.update(Gdx.graphics.getDeltaTime());
152 |
153 | float sw = Gdx.graphics.getWidth();
154 | float sh = Gdx.graphics.getHeight();
155 | float x = isAnchorLeft() ? offsetX : sw - w - offsetX;
156 | float bgX = isAnchorLeft() ? x - w / 10 : x;
157 |
158 | layout.setText(font, text);
159 | float width = layout.width;// contains the width of the current set text
160 | float textH = layout.height; // contains the height of the current set text
161 |
162 | bg.setPosition(bgX, sh - y);
163 | bg.draw(batch);
164 |
165 | if (icon != null) {
166 | icon.setPosition(x + 10, sh - y + h / 2 - icon.getHeight() / 2);
167 | icon.draw(batch);
168 | font.setColor(Color.WHITE);
169 | font.draw(batch, text, x + 10 + icon.getWidth() + 10, sh - y + h / 2 + textH / 2);
170 | } else {
171 | font.setColor(Color.WHITE);
172 | font.draw(batch, text, x + 10, sh - y + h / 2 + textH / 2);
173 | }
174 | }
175 |
176 | public boolean touchMoved(int x, int y) {
177 | y = Gdx.graphics.getHeight() - y - 1;
178 | if (isOver(x, y) && !isTouchOver && state == State.SHOWN) {
179 | isTouchOver = true;
180 | tiltOn();
181 | if (callback != null) callback.touchEnter(this);
182 | } else if (!isOver(x, y) && isTouchOver) {
183 | isTouchOver = false;
184 | tiltOff();
185 | if (callback != null) callback.touchExit(this);
186 | }
187 | return isOver(x, y);
188 | }
189 |
190 | public boolean touchDown(int x, int y) {
191 | y = Gdx.graphics.getHeight() - y - 1;
192 | if (isOver(x, y) && callback != null) callback.touchDown(this);
193 | return isOver(x, y);
194 | }
195 |
196 | // -------------------------------------------------------------------------
197 | // Helpers
198 | // -------------------------------------------------------------------------
199 |
200 | private boolean isOver(float x, float y) {
201 | return SpriteUtils.isOver(bg, x, y);
202 | }
203 |
204 | private boolean isAnchorLeft() {
205 | return anchor == Anchor.BOTTOM_LEFT || anchor == Anchor.TOP_LEFT;
206 | }
207 |
208 | // -------------------------------------------------------------------------
209 | // Tween Accessor
210 | // -------------------------------------------------------------------------
211 |
212 | private static class Accessor implements TweenAccessor {
213 | public static final int OFFSET_X = 1;
214 | public static final int ALPHA = 2;
215 |
216 | @Override
217 | public int getValues(Label target, int tweenType, float[] returnValues) {
218 | switch (tweenType) {
219 | case OFFSET_X:
220 | returnValues[0] = target.offsetX;
221 | return 1;
222 | case ALPHA:
223 | returnValues[0] = target.bg.getColor().a;
224 | return 1;
225 | default:
226 | assert false;
227 | return -1;
228 | }
229 | }
230 |
231 | @Override
232 | public void setValues(Label target, int tweenType, float[] newValues) {
233 | switch (tweenType) {
234 | case OFFSET_X:
235 | target.offsetX = newValues[0];
236 | break;
237 | case ALPHA:
238 | Color c = target.bg.getColor();
239 | target.bg.setColor(c.r, c.g, c.b, newValues[0]);
240 | break;
241 | default:
242 | assert false;
243 | }
244 | }
245 | }
246 |
247 | ;
248 | }
249 |
--------------------------------------------------------------------------------
/editor/src/main/java/aurelienribon/bodyeditor/canvas/PanZoomInputProcessor.java:
--------------------------------------------------------------------------------
1 | package aurelienribon.bodyeditor.canvas;
2 |
3 | import com.badlogic.gdx.Gdx;
4 | import com.badlogic.gdx.Input.Buttons;
5 | import com.badlogic.gdx.InputAdapter;
6 | import com.badlogic.gdx.math.Vector2;
7 |
8 | /**
9 | * @author Aurelien Ribon | http://www.aurelienribon.com/
10 | */
11 | public class PanZoomInputProcessor extends InputAdapter {
12 | private final Canvas canvas;
13 | private final Vector2 lastTouch = new Vector2();
14 | private final int[] zoomLevels = {5, 10, 16, 25, 33, 50, 66, 100, 150, 200, 300, 400, 600, 800, 1000};
15 | private int zoomLevel = 100;
16 |
17 | public PanZoomInputProcessor(Canvas canvas) {
18 | this.canvas = canvas;
19 | }
20 |
21 | @Override
22 | public boolean touchDown(int x, int y, int pointer, int button) {
23 | if (button != Buttons.RIGHT) return false;
24 |
25 | Vector2 p = canvas.screenToWorld(x, y);
26 | lastTouch.set(p);
27 | return false;
28 | }
29 |
30 | @Override
31 | public boolean touchDragged(int x, int y, int pointer) {
32 | if (!Gdx.input.isButtonPressed(Buttons.RIGHT)) return false;
33 |
34 | Vector2 p = canvas.screenToWorld(x, y);
35 | Vector2 delta = new Vector2(p).sub(lastTouch);
36 | canvas.worldCamera.translate(-delta.x, -delta.y, 0);
37 | canvas.worldCamera.update();
38 | lastTouch.set(canvas.screenToWorld(x, y));
39 | return false;
40 | }
41 |
42 | @Override
43 | public boolean scrolled(float amountX, float amountY) {
44 | int amount = (int) amountY;
45 | if (zoomLevel == zoomLevels[0] && amount < 0) {
46 | zoomLevel = zoomLevels[1];
47 | } else if (zoomLevel == zoomLevels[zoomLevels.length - 1] && amount > 0) {
48 | zoomLevel = zoomLevels[zoomLevels.length - 2];
49 | } else {
50 | for (int i = 1; i < zoomLevels.length - 1; i++) {
51 | if (zoomLevels[i] == zoomLevel) {
52 | zoomLevel = amount > 0 ? zoomLevels[i - 1] : zoomLevels[i + 1];
53 | break;
54 | }
55 | }
56 | }
57 |
58 | canvas.worldCamera.zoom = 100f / zoomLevel;
59 | canvas.worldCamera.update();
60 | return false;
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/editor/src/main/java/aurelienribon/bodyeditor/canvas/dynamicobjects/BodiesList.java:
--------------------------------------------------------------------------------
1 | package aurelienribon.bodyeditor.canvas.dynamicobjects;
2 |
3 | import aurelienribon.bodyeditor.Ctx;
4 | import aurelienribon.bodyeditor.canvas.Assets;
5 | import aurelienribon.bodyeditor.canvas.Canvas;
6 | import aurelienribon.bodyeditor.models.DynamicObjectModel;
7 | import aurelienribon.bodyeditor.models.DynamicObjectModel.BodyTuple;
8 | import aurelienribon.bodyeditor.models.RigidBodyModel;
9 | import aurelienribon.tweenengine.Tween;
10 | import aurelienribon.tweenengine.TweenManager;
11 | import aurelienribon.tweenengine.primitives.MutableFloat;
12 | import com.badlogic.gdx.Gdx;
13 | import com.badlogic.gdx.graphics.Color;
14 | import com.badlogic.gdx.graphics.Texture;
15 | import com.badlogic.gdx.graphics.g2d.BitmapFont;
16 | import com.badlogic.gdx.graphics.g2d.Sprite;
17 | import com.badlogic.gdx.graphics.g2d.SpriteBatch;
18 | import com.badlogic.gdx.math.Vector2;
19 |
20 | import java.util.ArrayList;
21 | import java.util.List;
22 |
23 | /**
24 | * @author Aurelien Ribon | http://www.aurelienribon.com/
25 | */
26 | public class BodiesList {
27 | private final Canvas canvas;
28 | private final List items = new ArrayList();
29 | private final Sprite background = new Sprite(Assets.inst().get("data/white.png", Texture.class));
30 | private final MutableFloat offsetX = new MutableFloat(0);
31 |
32 | private final TweenManager tweenManager = new TweenManager();
33 | private final BitmapFont font;
34 | private float width = 70;
35 | private float padding = 5;
36 | private Color bgColor = new Color(0x2A / 255f, 0x3B / 255f, 0x56 / 255f, 180 / 255f);
37 | private float itemPadding = 5;
38 | private Color itemBgColor = new Color(0x2A / 255f, 0x3B / 255f, 0x56 / 255f, 180 / 255f);
39 | private BodyTuple draggedBody;
40 | private boolean draggedBodyAdded = false;
41 |
42 | public BodiesList(Canvas canvas, BitmapFont font) {
43 | this.canvas = canvas;
44 | this.font = font;
45 | background.setColor(bgColor);
46 | }
47 |
48 | // -------------------------------------------------------------------------
49 | // Public API
50 | // -------------------------------------------------------------------------
51 |
52 | public void touchMoved(int x, int y) {
53 | for (BodiesListItem item : items) item.touchMoved(x, y);
54 | }
55 |
56 | public boolean touchDown(int x, int y) {
57 | draggedBody = null;
58 | draggedBodyAdded = false;
59 |
60 | for (BodiesListItem item : items) {
61 | if (item.touchDown(x, y)) {
62 | draggedBody = new BodyTuple(item.getModel());
63 | return true;
64 | }
65 | }
66 |
67 | return false;
68 | }
69 |
70 | public void touchDragged(int x, int y) {
71 | if (draggedBody == null) return;
72 |
73 | DynamicObjectModel model = Ctx.objects.getSelectedModel();
74 | if (model == null) return;
75 |
76 | if (x < background.getX() && !draggedBodyAdded) {
77 | draggedBodyAdded = true;
78 | model.addTuple(draggedBody);
79 | }
80 |
81 | if (draggedBodyAdded) {
82 | Vector2 p = canvas.screenToWorld(x, y);
83 | Vector2 o = draggedBody.model.getOrigin();
84 | draggedBody.attrs.scale = 0.25f;
85 | draggedBody.attrs.x = p.x - o.x * 0.25f;
86 | draggedBody.attrs.y = p.y - o.y * 0.25f;
87 | }
88 | }
89 |
90 | public void updateItems() {
91 | items.clear();
92 |
93 | for (RigidBodyModel model : Ctx.bodies.getModels()) {
94 | float w = width - padding * 2;
95 | float p = itemPadding;
96 | Color c = itemBgColor;
97 | BodiesListItem item = new BodiesListItem(model, w, w, p, font, c);
98 | items.add(item);
99 | }
100 | }
101 |
102 | public void show() {
103 | Tween.to(offsetX, 0, 0.3f).target(-width).start(tweenManager);
104 | }
105 |
106 | public void hide() {
107 | Tween.to(offsetX, 0, 0.3f).target(0).start(tweenManager);
108 | }
109 |
110 | public void draw(SpriteBatch batch) {
111 | tweenManager.update(Gdx.graphics.getDeltaTime());
112 |
113 | float w = Gdx.graphics.getWidth();
114 | float h = Gdx.graphics.getHeight();
115 |
116 | background.setPosition(w + offsetX.floatValue(), 0);
117 | background.setSize(width, h);
118 | background.draw(batch);
119 |
120 | for (int i = 0; i < items.size(); i++) {
121 | BodiesListItem item = items.get(i);
122 | float x = background.getX() + padding;
123 | float y = background.getY() + background.getHeight() - (item.getHeight() + padding) * (i + 1);
124 | item.draw(batch, x, y);
125 | }
126 | }
127 | }
128 |
--------------------------------------------------------------------------------
/editor/src/main/java/aurelienribon/bodyeditor/canvas/dynamicobjects/BodiesListItem.java:
--------------------------------------------------------------------------------
1 | package aurelienribon.bodyeditor.canvas.dynamicobjects;
2 |
3 | import aurelienribon.accessors.SpriteAccessor;
4 | import aurelienribon.bodyeditor.canvas.Assets;
5 | import aurelienribon.bodyeditor.models.RigidBodyModel;
6 | import aurelienribon.tweenengine.Tween;
7 | import aurelienribon.tweenengine.TweenManager;
8 | import aurelienribon.utils.gdx.SpriteUtils;
9 | import com.badlogic.gdx.Gdx;
10 | import com.badlogic.gdx.graphics.Color;
11 | import com.badlogic.gdx.graphics.Texture;
12 | import com.badlogic.gdx.graphics.g2d.BitmapFont;
13 | import com.badlogic.gdx.graphics.g2d.GlyphLayout;
14 | import com.badlogic.gdx.graphics.g2d.Sprite;
15 | import com.badlogic.gdx.graphics.g2d.SpriteBatch;
16 | import com.badlogic.gdx.graphics.g2d.TextureRegion;
17 |
18 | /**
19 | * @author Aurelien Ribon | http://www.aurelienribon.com/
20 | */
21 | public class BodiesListItem {
22 | private static final float IMAGE_OPACITY = 0.2f;
23 |
24 | private final RigidBodyModel model;
25 | private final BitmapFont font;
26 | private final float padding;
27 | private final Color bgColor;
28 |
29 | private final TweenManager tweenManager = new TweenManager();
30 | private final Sprite background = new Sprite(Assets.inst().get("data/white.png", Texture.class));
31 | private final Sprite image;
32 | private String text = "";
33 | private boolean isTouchOver = false;
34 |
35 | public BodiesListItem(RigidBodyModel model, float w, float h, float p, BitmapFont font, Color bgColor) {
36 | this.model = model;
37 | this.font = font;
38 | this.text = model.getName();
39 | this.padding = p;
40 | this.bgColor = bgColor;
41 |
42 | background.setSize(w, h);
43 | background.setColor(bgColor);
44 |
45 | TextureRegion region = Assets.inst().getRegion(model);
46 | if (region != null) {
47 | image = new Sprite(region);
48 | float imgW = image.getWidth() > image.getHeight() ? w - p * 2 : (h - p * 2) * image.getWidth() / image.getHeight();
49 | float imgH = image.getWidth() > image.getHeight() ? (w - p * 2) * image.getHeight() / image.getWidth() : h - p * 2;
50 | image.setSize(imgW, imgH);
51 | image.setColor(1, 1, 1, IMAGE_OPACITY);
52 | } else {
53 | image = null;
54 | }
55 | GlyphLayout glyph = new GlyphLayout();
56 | glyph.setText(font, text);
57 | if (glyph.width > w - p * 2) {
58 | for (int i = 1; i <= model.getName().length(); i++) {
59 | text = model.getName().substring(0, i) + "...";
60 | if (glyph.width > w - p * 2) {
61 | text = model.getName().substring(0, i - 1) + "...";
62 | break;
63 | }
64 | }
65 | }
66 | }
67 |
68 | // -------------------------------------------------------------------------
69 | // Public API
70 | // -------------------------------------------------------------------------
71 |
72 | public void draw(SpriteBatch batch, float x, float y) {
73 | tweenManager.update(Gdx.graphics.getDeltaTime());
74 |
75 | background.setPosition(x, y);
76 | background.draw(batch);
77 |
78 | if (image != null) {
79 | float imgX = x + background.getWidth() / 2 - image.getWidth() / 2;
80 | float imgY = y + background.getHeight() / 2 - image.getHeight() / 2;
81 | image.setPosition(imgX, imgY);
82 | image.draw(batch);
83 | }
84 |
85 | font.setColor(Color.WHITE);
86 | font.draw(batch, text, x + padding, y + background.getHeight() - padding);
87 | }
88 |
89 | public float getWidth() {
90 | return background.getWidth();
91 | }
92 |
93 | public float getHeight() {
94 | return background.getHeight();
95 | }
96 |
97 | public RigidBodyModel getModel() {
98 | return model;
99 | }
100 |
101 | public boolean touchMoved(int x, int y) {
102 | y = Gdx.graphics.getHeight() - y - 1;
103 | if (isOver(x, y) && !isTouchOver) {
104 | isTouchOver = true;
105 | tweenManager.killAll();
106 | Tween.to(background, SpriteAccessor.OPACITY, 0.3f).target(1).start(tweenManager);
107 | Tween.to(image, SpriteAccessor.OPACITY, 0.3f).target(1).start(tweenManager);
108 | } else if (!isOver(x, y) && isTouchOver) {
109 | isTouchOver = false;
110 | tweenManager.killAll();
111 | Tween.to(background, SpriteAccessor.OPACITY, 0.3f).target(bgColor.a).start(tweenManager);
112 | Tween.to(image, SpriteAccessor.OPACITY, 0.3f).target(IMAGE_OPACITY).start(tweenManager);
113 | }
114 | return isOver(x, y);
115 | }
116 |
117 | public boolean touchDown(int x, int y) {
118 | y = Gdx.graphics.getHeight() - y - 1;
119 | return isOver(x, y);
120 | }
121 |
122 | // -------------------------------------------------------------------------
123 | // Helpers
124 | // -------------------------------------------------------------------------
125 |
126 | private boolean isOver(float x, float y) {
127 | return SpriteUtils.isOver(background, x, y);
128 | }
129 | }
130 |
--------------------------------------------------------------------------------
/editor/src/main/java/aurelienribon/bodyeditor/canvas/dynamicobjects/DynamicObjectsScreen.java:
--------------------------------------------------------------------------------
1 | package aurelienribon.bodyeditor.canvas.dynamicobjects;
2 |
3 | import aurelienribon.bodyeditor.Ctx;
4 | import aurelienribon.bodyeditor.canvas.Assets;
5 | import aurelienribon.bodyeditor.canvas.Canvas;
6 | import aurelienribon.bodyeditor.models.DynamicObjectModel;
7 | import aurelienribon.bodyeditor.models.DynamicObjectModel.BodyTuple;
8 | import aurelienribon.tweenengine.TweenManager;
9 | import com.badlogic.gdx.Gdx;
10 | import com.badlogic.gdx.Input.Buttons;
11 | import com.badlogic.gdx.InputAdapter;
12 | import com.badlogic.gdx.InputProcessor;
13 | import com.badlogic.gdx.graphics.g2d.Sprite;
14 | import com.badlogic.gdx.graphics.g2d.TextureRegion;
15 |
16 | /**
17 | * @author Aurelien Ribon | http://www.aurelienribon.com/
18 | */
19 | public class DynamicObjectsScreen {
20 |
21 | private final Canvas canvas;
22 | private final TweenManager tweenManager = new TweenManager();
23 | private final BodiesList bodiesList;
24 |
25 | public DynamicObjectsScreen(Canvas canvas) {
26 | this.canvas = canvas;
27 | this.bodiesList = new BodiesList(canvas, canvas.font);
28 |
29 | canvas.input.addProcessor(bodiesListInputProcessor);
30 |
31 | canvas.addListener(new Canvas.Listener() {
32 | @Override
33 | public void modeChanged(Canvas.Mode mode) {
34 | if (mode == Canvas.Mode.OBJECTS) {
35 | bodiesList.updateItems();
36 | bodiesList.show();
37 | } else {
38 | bodiesList.hide();
39 | }
40 | }
41 | });
42 | }
43 |
44 | private final InputProcessor bodiesListInputProcessor = new InputAdapter() {
45 | //@Override
46 | public boolean touchMoved(int x, int y) {
47 | bodiesList.touchMoved(x, y);
48 | return false;
49 | }
50 |
51 | @Override
52 | public boolean touchDown(int x, int y, int pointer, int button) {
53 | if (button == Buttons.LEFT) if (bodiesList.touchDown(x, y)) return true;
54 | return false;
55 | }
56 |
57 | @Override
58 | public boolean touchDragged(int x, int y, int pointer) {
59 | if (Gdx.input.isButtonPressed(Buttons.LEFT)) bodiesList.touchDragged(x, y);
60 | return false;
61 | }
62 | };
63 |
64 | // -------------------------------------------------------------------------
65 | // Render
66 | // -------------------------------------------------------------------------
67 |
68 | public void render() {
69 | tweenManager.update(Gdx.graphics.getDeltaTime());
70 |
71 | DynamicObjectModel model = Ctx.objects.getSelectedModel();
72 |
73 | if (model != null) {
74 | for (BodyTuple tuple : model.getTuples()) {
75 | TextureRegion region = Assets.inst().getRegion(tuple.model);
76 | if (region != null) {
77 |
78 | // TODO: Clean this mess!
79 |
80 | Sprite sp = new Sprite(region);
81 | sp.setSize(tuple.attrs.scale, tuple.attrs.scale * sp.getHeight() / sp.getWidth());
82 | sp.setPosition(tuple.attrs.x, tuple.attrs.y);
83 | canvas.batch.setProjectionMatrix(canvas.worldCamera.combined);
84 | canvas.batch.begin();
85 | sp.draw(canvas.batch);
86 | canvas.batch.end();
87 | }
88 |
89 | canvas.drawer.drawModel(tuple.model, tuple.attrs);
90 | }
91 | }
92 |
93 | canvas.batch.setProjectionMatrix(canvas.screenCamera.combined);
94 | canvas.batch.begin();
95 | bodiesList.draw(canvas.batch);
96 | canvas.batch.end();
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/editor/src/main/java/aurelienribon/bodyeditor/canvas/rigidbodies/RigidBodiesScreenDrawer.java:
--------------------------------------------------------------------------------
1 | package aurelienribon.bodyeditor.canvas.rigidbodies;
2 |
3 | import com.badlogic.gdx.Gdx;
4 | import com.badlogic.gdx.graphics.Color;
5 | import com.badlogic.gdx.graphics.OrthographicCamera;
6 | import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
7 | import com.badlogic.gdx.math.Vector2;
8 |
9 | /**
10 | * @author Aurelien Ribon | http://www.aurelienribon.com/
11 | */
12 | public class RigidBodiesScreenDrawer {
13 | private static final Color BALLTHROWPATH_COLOR = new Color(0.2f, 0.2f, 0.2f, 1);
14 |
15 | private final ShapeRenderer drawer = new ShapeRenderer();
16 | private final OrthographicCamera camera;
17 |
18 | public RigidBodiesScreenDrawer(OrthographicCamera camera) {
19 | this.camera = camera;
20 | }
21 |
22 | // -------------------------------------------------------------------------
23 | // Public API
24 | // -------------------------------------------------------------------------
25 |
26 | public void drawBallThrowPath(Vector2 p1, Vector2 p2) {
27 | if (p1 == null || p2 == null) return;
28 |
29 | Gdx.gl30.glLineWidth(3);
30 | Gdx.gl30.glEnable(Gdx.gl30.GL_BLEND);
31 | Gdx.gl30.glBlendFunc(Gdx.gl30.GL_SRC_ALPHA, Gdx.gl30.GL_ONE_MINUS_SRC_ALPHA);
32 |
33 | float w = 0.03f * camera.zoom;
34 |
35 | drawer.setProjectionMatrix(camera.combined);
36 | drawer.begin(ShapeRenderer.ShapeType.Line);
37 | drawer.setColor(BALLTHROWPATH_COLOR);
38 | drawer.line(p1.x, p1.y, p2.x, p2.y);
39 | drawer.end();
40 |
41 | drawer.setProjectionMatrix(camera.combined);
42 | drawer.begin(ShapeRenderer.ShapeType.Filled);
43 | drawer.setColor(BALLTHROWPATH_COLOR);
44 | drawer.rect(p2.cpy().sub(w / 2, w / 2).x, p2.cpy().sub(w / 2, w / 2).y, w, w);
45 | drawer.end();
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/editor/src/main/java/aurelienribon/bodyeditor/canvas/rigidbodies/input/CreationInputProcessor.java:
--------------------------------------------------------------------------------
1 | package aurelienribon.bodyeditor.canvas.rigidbodies.input;
2 |
3 | import aurelienribon.bodyeditor.Ctx;
4 | import aurelienribon.bodyeditor.canvas.Canvas;
5 | import aurelienribon.bodyeditor.canvas.InputHelper;
6 | import aurelienribon.bodyeditor.canvas.rigidbodies.RigidBodiesScreen;
7 | import aurelienribon.bodyeditor.models.RigidBodyModel;
8 | import aurelienribon.bodyeditor.models.ShapeModel;
9 | import com.badlogic.gdx.Input;
10 | import com.badlogic.gdx.Input.Buttons;
11 | import com.badlogic.gdx.InputAdapter;
12 | import com.badlogic.gdx.math.Vector2;
13 |
14 | import java.util.List;
15 |
16 | /**
17 | * @author Aurelien Ribon | http://www.aurelienribon.com/
18 | */
19 | public class CreationInputProcessor extends InputAdapter {
20 | private final Canvas canvas;
21 | private final RigidBodiesScreen screen;
22 | private boolean touchDown = false;
23 |
24 | public CreationInputProcessor(Canvas canvas, RigidBodiesScreen screen) {
25 | this.canvas = canvas;
26 | this.screen = screen;
27 | }
28 |
29 | @Override
30 | public boolean touchDown(int x, int y, int pointer, int button) {
31 | touchDown = button == Buttons.LEFT;
32 | if (!touchDown) return false;
33 |
34 | RigidBodyModel model = Ctx.bodies.getSelectedModel();
35 | if (model == null) return false;
36 |
37 | List shapes = model.getShapes();
38 | ShapeModel lastShape = shapes.isEmpty() ? null : shapes.get(shapes.size() - 1);
39 |
40 | if (lastShape == null || lastShape.isClosed()) {
41 | ShapeModel.Type type = InputHelper.isCtrlDown() ? ShapeModel.Type.CIRCLE : ShapeModel.Type.POLYGON;
42 | lastShape = new ShapeModel(type);
43 | lastShape.getVertices().add(canvas.alignedScreenToWorld(x, y));
44 | shapes.add(lastShape);
45 |
46 | } else {
47 | List vs = lastShape.getVertices();
48 | Vector2 np = screen.nearestPoint;
49 | ShapeModel.Type type = lastShape.getType();
50 |
51 | if (type == ShapeModel.Type.POLYGON && vs.size() >= 3 && np == vs.get(0)) {
52 | lastShape.close();
53 | model.computePhysics();
54 | screen.buildBody();
55 | } else if (type == ShapeModel.Type.CIRCLE) {
56 | vs.add(canvas.alignedScreenToWorld(x, y));
57 | lastShape.close();
58 | model.computePhysics();
59 | screen.buildBody();
60 | } else {
61 | vs.add(canvas.alignedScreenToWorld(x, y));
62 | }
63 | }
64 |
65 | return false;
66 | }
67 |
68 | @Override
69 | public boolean touchUp(int x, int y, int pointer, int button) {
70 | touchDown = false;
71 | return false;
72 | }
73 |
74 | @Override
75 | public boolean touchDragged(int x, int y, int pointer) {
76 | if (!touchDown) return false;
77 | mouseMoved(x, y);
78 | return false;
79 | }
80 |
81 | @Override // possibly mouseMoved?
82 | public boolean mouseMoved(int x, int y) {
83 | RigidBodyModel model = Ctx.bodies.getSelectedModel();
84 | if (model == null) return false;
85 |
86 | // Nearest point computation
87 |
88 | screen.nearestPoint = null;
89 | Vector2 p = canvas.screenToWorld(x, y);
90 |
91 | List shapes = model.getShapes();
92 | ShapeModel lastShape = shapes.isEmpty() ? null : shapes.get(shapes.size() - 1);
93 |
94 | if (lastShape != null) {
95 | List vs = lastShape.getVertices();
96 | float zoom = canvas.worldCamera.zoom;
97 |
98 | if (!lastShape.isClosed() && vs.size() >= 3)
99 | if (vs.get(0).dst(p) < 0.025f * zoom)
100 | screen.nearestPoint = vs.get(0);
101 | }
102 |
103 | // Next point assignment
104 |
105 | screen.nextPoint = canvas.alignedScreenToWorld(x, y);
106 | return false;
107 | }
108 |
109 | @Override
110 | public boolean keyDown(int keycode) {
111 | switch (keycode) {
112 | case Input.Keys.ESCAPE:
113 | RigidBodyModel model = Ctx.bodies.getSelectedModel();
114 | if (model == null) break;
115 | if (model.getShapes().isEmpty()) break;
116 | if (model.getShapes().get(model.getShapes().size() - 1).isClosed()) break;
117 | model.getShapes().remove(model.getShapes().size() - 1);
118 | break;
119 | }
120 | return false;
121 | }
122 | }
123 |
--------------------------------------------------------------------------------
/editor/src/main/java/aurelienribon/bodyeditor/canvas/rigidbodies/input/EditionInputProcessor.java:
--------------------------------------------------------------------------------
1 | package aurelienribon.bodyeditor.canvas.rigidbodies.input;
2 |
3 | import aurelienribon.bodyeditor.Ctx;
4 | import aurelienribon.bodyeditor.canvas.Canvas;
5 | import aurelienribon.bodyeditor.canvas.InputHelper;
6 | import aurelienribon.bodyeditor.canvas.rigidbodies.RigidBodiesScreen;
7 | import aurelienribon.bodyeditor.models.RigidBodyModel;
8 | import aurelienribon.bodyeditor.models.ShapeModel;
9 | import com.badlogic.gdx.Input.Buttons;
10 | import com.badlogic.gdx.Input.Keys;
11 | import com.badlogic.gdx.InputAdapter;
12 | import com.badlogic.gdx.math.Rectangle;
13 | import com.badlogic.gdx.math.Vector2;
14 |
15 | import java.util.ArrayList;
16 | import java.util.Collections;
17 | import java.util.List;
18 |
19 | /**
20 | * @author Aurelien Ribon | http://www.aurelienribon.com/
21 | */
22 | public class EditionInputProcessor extends InputAdapter {
23 | private final Canvas canvas;
24 | private final RigidBodiesScreen screen;
25 | private boolean touchDown = false;
26 | private Vector2 draggedPoint;
27 |
28 | public EditionInputProcessor(Canvas canvas, RigidBodiesScreen screen) {
29 | this.canvas = canvas;
30 | this.screen = screen;
31 | }
32 |
33 | @Override
34 | public boolean touchDown(int x, int y, int pointer, int button) {
35 | touchDown = button == Buttons.LEFT;
36 | if (!touchDown) return false;
37 |
38 | RigidBodyModel model = Ctx.bodies.getSelectedModel();
39 | if (model == null) return false;
40 |
41 | draggedPoint = screen.nearestPoint;
42 |
43 | if (draggedPoint == null) {
44 | screen.mouseSelectionP1 = canvas.screenToWorld(x, y);
45 |
46 | } else {
47 | if (draggedPoint == model.getOrigin()) {
48 | screen.selectedPoints.clear();
49 | } else if (InputHelper.isCtrlDown()) {
50 | if (screen.selectedPoints.contains(draggedPoint)) screen.selectedPoints.remove(draggedPoint);
51 | else screen.selectedPoints.add(draggedPoint);
52 | } else if (!screen.selectedPoints.contains(draggedPoint)) {
53 | screen.selectedPoints.replaceBy(draggedPoint);
54 | }
55 | }
56 |
57 | return false;
58 | }
59 |
60 | @Override
61 | public boolean touchUp(int x, int y, int pointer, int button) {
62 | if (!touchDown) return false;
63 | touchDown = false;
64 |
65 | RigidBodyModel model = Ctx.bodies.getSelectedModel();
66 | if (model == null) return false;
67 |
68 | if (draggedPoint != null) {
69 | draggedPoint = null;
70 | model.computePhysics();
71 | screen.buildBody();
72 |
73 | } else if (screen.mouseSelectionP2 != null) {
74 | if (InputHelper.isCtrlDown()) {
75 | for (Vector2 p : getPointsInSelection()) {
76 | if (screen.selectedPoints.contains(p)) screen.selectedPoints.remove(p);
77 | else screen.selectedPoints.add(p);
78 | }
79 | } else {
80 | screen.selectedPoints.replaceBy(getPointsInSelection());
81 | }
82 |
83 | } else {
84 | screen.selectedPoints.clear();
85 | }
86 |
87 | screen.mouseSelectionP1 = null;
88 | screen.mouseSelectionP2 = null;
89 | return false;
90 | }
91 |
92 | @Override
93 | public boolean touchDragged(int x, int y, int pointer) {
94 | if (!touchDown) return false;
95 |
96 | RigidBodyModel model = Ctx.bodies.getSelectedModel();
97 | if (model == null) return false;
98 |
99 | if (draggedPoint != null) {
100 | Vector2 p = canvas.alignedScreenToWorld(x, y);
101 | model.clearPhysics();
102 |
103 | float dx = p.x - draggedPoint.x;
104 | float dy = p.y - draggedPoint.y;
105 | draggedPoint.add(dx, dy);
106 |
107 | for (int i = 0; i < screen.selectedPoints.size(); i++) {
108 | Vector2 sp = screen.selectedPoints.get(i);
109 | if (sp != draggedPoint) sp.add(dx, dy);
110 | }
111 |
112 | } else {
113 | screen.mouseSelectionP2 = canvas.screenToWorld(x, y);
114 | }
115 |
116 | return false;
117 | }
118 |
119 | @Override
120 | public boolean mouseMoved(int x, int y) {
121 | RigidBodyModel model = Ctx.bodies.getSelectedModel();
122 | if (model == null) return false;
123 |
124 | // Nearest point computation
125 |
126 | Vector2 p = canvas.screenToWorld(x, y);
127 | screen.nearestPoint = null;
128 | float dist = 0.025f * canvas.worldCamera.zoom;
129 |
130 | for (Vector2 v : getAllPoints()) {
131 | if (v.dst(p) < dist) screen.nearestPoint = v;
132 | }
133 |
134 | return false;
135 | }
136 |
137 | @Override
138 | public boolean keyDown(int keycode) {
139 | switch (keycode) {
140 | case Keys.ENTER:
141 | screen.insertPointsBetweenSelected();
142 | break;
143 | case Keys.BACKSPACE:
144 | screen.removeSelectedPoints();
145 | break;
146 | }
147 |
148 | return false;
149 | }
150 |
151 | // -------------------------------------------------------------------------
152 |
153 | private List getPointsInSelection() {
154 | RigidBodyModel model = Ctx.bodies.getSelectedModel();
155 | List points = new ArrayList();
156 | Vector2 p1 = screen.mouseSelectionP1;
157 | Vector2 p2 = screen.mouseSelectionP2;
158 |
159 | if (p1 != null && p2 != null) {
160 | Rectangle rect = new Rectangle(
161 | Math.min(p1.x, p2.x),
162 | Math.min(p1.y, p2.y),
163 | Math.abs(p2.x - p1.x),
164 | Math.abs(p2.y - p1.y)
165 | );
166 |
167 | for (Vector2 p : getAllPoints()) {
168 | if (p == model.getOrigin()) continue;
169 | if (rect.contains(p.x, p.y)) points.add(p);
170 | }
171 | }
172 |
173 | return Collections.unmodifiableList(points);
174 | }
175 |
176 | private List getAllPoints() {
177 | List points = new ArrayList();
178 | RigidBodyModel model = Ctx.bodies.getSelectedModel();
179 |
180 | for (ShapeModel shape : model.getShapes()) {
181 | points.addAll(shape.getVertices());
182 | }
183 |
184 | points.add(model.getOrigin());
185 | return Collections.unmodifiableList(points);
186 | }
187 | }
188 |
--------------------------------------------------------------------------------
/editor/src/main/java/aurelienribon/bodyeditor/canvas/rigidbodies/input/TestInputProcessor.java:
--------------------------------------------------------------------------------
1 | package aurelienribon.bodyeditor.canvas.rigidbodies.input;
2 |
3 | import aurelienribon.bodyeditor.Ctx;
4 | import aurelienribon.bodyeditor.canvas.Canvas;
5 | import aurelienribon.bodyeditor.canvas.rigidbodies.RigidBodiesScreen;
6 | import aurelienribon.bodyeditor.models.RigidBodyModel;
7 | import com.badlogic.gdx.Input.Buttons;
8 | import com.badlogic.gdx.InputAdapter;
9 | import com.badlogic.gdx.math.Vector2;
10 |
11 | /**
12 | * @author Aurelien Ribon | http://www.aurelienribon.com/
13 | */
14 | public class TestInputProcessor extends InputAdapter {
15 | private final Canvas canvas;
16 | private final RigidBodiesScreen screen;
17 | private boolean touchDown = false;
18 |
19 | public TestInputProcessor(Canvas canvas, RigidBodiesScreen screen) {
20 | this.canvas = canvas;
21 | this.screen = screen;
22 | }
23 |
24 | @Override
25 | public boolean touchDown(int x, int y, int pointer, int button) {
26 | touchDown = button == Buttons.LEFT;
27 | if (!touchDown) return false;
28 |
29 | RigidBodyModel model = Ctx.bodies.getSelectedModel();
30 | if (model == null) return false;
31 |
32 | Vector2 p = canvas.screenToWorld(x, y);
33 | screen.ballThrowP1 = p;
34 | screen.ballThrowP2 = p;
35 | return false;
36 | }
37 |
38 | @Override
39 | public boolean touchUp(int x, int y, int pointer, int button) {
40 | if (!touchDown) return false;
41 | touchDown = false;
42 |
43 | RigidBodyModel model = Ctx.bodies.getSelectedModel();
44 | if (model == null) return false;
45 |
46 | Vector2 p1 = screen.ballThrowP1;
47 | Vector2 p2 = screen.ballThrowP2;
48 | Vector2 delta = new Vector2(p2).sub(p1);
49 | screen.fireBall(p1, delta.scl(3f));
50 |
51 | screen.ballThrowP1 = null;
52 | screen.ballThrowP2 = null;
53 | return false;
54 | }
55 |
56 | @Override
57 | public boolean touchDragged(int x, int y, int pointer) {
58 | if (!touchDown) return false;
59 |
60 | RigidBodyModel model = Ctx.bodies.getSelectedModel();
61 | if (model == null) return false;
62 |
63 | Vector2 p = canvas.screenToWorld(x, y);
64 | screen.ballThrowP2 = p;
65 | return false;
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/editor/src/main/java/aurelienribon/bodyeditor/io/JsonIo.java:
--------------------------------------------------------------------------------
1 | package aurelienribon.bodyeditor.io;
2 |
3 | import aurelienribon.bodyeditor.Ctx;
4 | import aurelienribon.bodyeditor.models.CircleModel;
5 | import aurelienribon.bodyeditor.models.DynamicObjectModel;
6 | import aurelienribon.bodyeditor.models.PolygonModel;
7 | import aurelienribon.bodyeditor.models.RigidBodyModel;
8 | import aurelienribon.bodyeditor.models.ShapeModel;
9 | import com.badlogic.gdx.math.Vector2;
10 | import org.apache.commons.io.FilenameUtils;
11 | import org.json.JSONArray;
12 | import org.json.JSONException;
13 | import org.json.JSONObject;
14 | import org.json.JSONStringer;
15 |
16 | /**
17 | * @author Aurelien Ribon | http://www.aurelienribon.com
18 | */
19 | public class JsonIo {
20 | public static String serialize() throws JSONException {
21 | JSONStringer json = new JSONStringer();
22 | json.object();
23 | json.key("rigidBodies").array();
24 |
25 | for (RigidBodyModel model : Ctx.bodies.getModels()) {
26 | json.object();
27 | json.key("name").value(model.getName());
28 | json.key("imagePath").value(FilenameUtils.separatorsToUnix(model.getImagePath()));
29 | json.key("origin").object().key("x").value(model.getOrigin().x).key("y").value(model.getOrigin().y).endObject();
30 | json.key("polygons").array();
31 |
32 | for (PolygonModel polygon : model.getPolygons()) {
33 | json.array();
34 | for (Vector2 vertex : polygon.vertices)
35 | json.object().key("x").value(vertex.x).key("y").value(vertex.y).endObject();
36 | json.endArray();
37 | }
38 |
39 | json.endArray();
40 | json.key("circles").array();
41 |
42 | for (CircleModel circle : model.getCircles()) {
43 | json.object();
44 | json.key("cx").value(circle.center.x);
45 | json.key("cy").value(circle.center.y);
46 | json.key("r").value(circle.radius);
47 | json.endObject();
48 | }
49 |
50 | json.endArray();
51 | json.key("shapes").array();
52 |
53 | for (ShapeModel shape : model.getShapes()) {
54 | json.object();
55 | json.key("type").value(shape.getType());
56 | json.key("vertices").array();
57 | for (Vector2 vertex : shape.getVertices())
58 | json.object().key("x").value(vertex.x).key("y").value(vertex.y).endObject();
59 | json.endArray();
60 | json.endObject();
61 | }
62 |
63 | json.endArray();
64 | json.endObject();
65 | }
66 |
67 | json.endArray();
68 | json.key("dynamicObjects").array();
69 |
70 | for (DynamicObjectModel model : Ctx.objects.getModels()) {
71 | json.object();
72 | json.key("name").value(model.getName());
73 | json.endObject();
74 | }
75 |
76 | json.endArray();
77 | json.endObject();
78 |
79 | return json.toString();
80 | }
81 |
82 | public static void deserialize(String str) throws JSONException {
83 | JSONObject json = new JSONObject(str);
84 |
85 | // rigid bodies
86 |
87 | JSONArray bodiesElem = json.getJSONArray("rigidBodies");
88 | for (int i = 0; i < bodiesElem.length(); i++) {
89 | JSONObject bodyElem = bodiesElem.getJSONObject(i);
90 |
91 | RigidBodyModel model = new RigidBodyModel();
92 | model.setName(bodyElem.getString("name"));
93 |
94 | String imgPath = bodyElem.isNull("imagePath") ? null : bodyElem.getString("imagePath");
95 | model.setImagePath(FilenameUtils.separatorsToSystem(imgPath));
96 |
97 | JSONObject originElem = bodyElem.getJSONObject("origin");
98 | model.getOrigin().x = (float) originElem.getDouble("x");
99 | model.getOrigin().y = (float) originElem.getDouble("y");
100 |
101 | JSONArray polygonsElem = bodyElem.getJSONArray("polygons");
102 |
103 | for (int ii = 0; ii < polygonsElem.length(); ii++) {
104 | PolygonModel polygon = new PolygonModel();
105 | model.getPolygons().add(polygon);
106 |
107 | JSONArray verticesElem = polygonsElem.getJSONArray(ii);
108 | for (int iii = 0; iii < verticesElem.length(); iii++) {
109 | JSONObject vertexElem = verticesElem.getJSONObject(iii);
110 | polygon.vertices.add(new Vector2(
111 | (float) vertexElem.getDouble("x"),
112 | (float) vertexElem.getDouble("y")));
113 | }
114 | }
115 |
116 | JSONArray circlesElem = bodyElem.getJSONArray("circles");
117 |
118 | for (int ii = 0; ii < circlesElem.length(); ii++) {
119 | CircleModel circle = new CircleModel();
120 | model.getCircles().add(circle);
121 |
122 | JSONObject circleElem = circlesElem.getJSONObject(ii);
123 | circle.center.x = (float) circleElem.getDouble("cx");
124 | circle.center.y = (float) circleElem.getDouble("cy");
125 | circle.radius = (float) circleElem.getDouble("r");
126 | }
127 |
128 | JSONArray shapesElem = bodyElem.getJSONArray("shapes");
129 |
130 | for (int ii = 0; ii < shapesElem.length(); ii++) {
131 | JSONObject shapeElem = shapesElem.getJSONObject(ii);
132 | ShapeModel.Type type = ShapeModel.Type.valueOf(shapeElem.getString("type"));
133 |
134 | ShapeModel shape = new ShapeModel(type);
135 | model.getShapes().add(shape);
136 |
137 | JSONArray verticesElem = shapeElem.getJSONArray("vertices");
138 | for (int iii = 0; iii < verticesElem.length(); iii++) {
139 | JSONObject vertexElem = verticesElem.getJSONObject(iii);
140 | shape.getVertices().add(new Vector2(
141 | (float) vertexElem.getDouble("x"),
142 | (float) vertexElem.getDouble("y")));
143 | }
144 |
145 | shape.close();
146 | }
147 |
148 | Ctx.bodies.getModels().add(model);
149 | }
150 |
151 | // dynamic objects
152 |
153 | JSONArray objectsElem = json.getJSONArray("dynamicObjects");
154 | for (int i = 0; i < objectsElem.length(); i++) {
155 | JSONObject objectElem = objectsElem.getJSONObject(i);
156 |
157 | DynamicObjectModel model = new DynamicObjectModel();
158 | model.setName(objectElem.getString("name"));
159 |
160 | Ctx.objects.getModels().add(model);
161 | }
162 | }
163 | }
164 |
--------------------------------------------------------------------------------
/editor/src/main/java/aurelienribon/bodyeditor/maths/Clipper.java:
--------------------------------------------------------------------------------
1 | package aurelienribon.bodyeditor.maths;
2 |
3 | import aurelienribon.bodyeditor.maths.earclipping.bayazit.BayazitDecomposer;
4 | import aurelienribon.bodyeditor.maths.earclipping.ewjordan.EwjordanDecomposer;
5 | import aurelienribon.utils.gdx.PolygonUtils;
6 | import com.badlogic.gdx.math.Vector2;
7 | import com.badlogic.gdx.utils.Array;
8 |
9 | import java.util.Arrays;
10 | import java.util.Collections;
11 | import java.util.List;
12 |
13 | /**
14 | * @author Aurelien Ribon | http://www.aurelienribon.com/
15 | */
16 | public class Clipper {
17 | public enum Polygonizer {EWJORDAN, BAYAZIT}
18 |
19 | public static Vector2[][] polygonize(Polygonizer polygonizer, Vector2[] points) {
20 | Vector2[][] polygons = null;
21 |
22 | if (PolygonUtils.isPolygonCCW(points)) {
23 | List vertices = Arrays.asList(points);
24 | Collections.reverse(vertices);
25 | points = vertices.toArray(new Vector2[0]);
26 | }
27 |
28 | switch (polygonizer) {
29 | case EWJORDAN:
30 | polygons = EwjordanDecomposer.decompose(points);
31 | break;
32 |
33 | case BAYAZIT:
34 | Array tmpPoints = new Array(points.length);
35 | tmpPoints.addAll(points);
36 |
37 | Array> tmpPolygons;
38 |
39 | try {
40 | tmpPolygons = BayazitDecomposer.ConvexPartition(tmpPoints);
41 | } catch (Exception ex) {
42 | tmpPolygons = null;
43 | }
44 |
45 | if (tmpPolygons != null) {
46 | polygons = new Vector2[tmpPolygons.size][];
47 | for (int i = 0; i < tmpPolygons.size; i++) {
48 | polygons[i] = new Vector2[tmpPolygons.get(i).size];
49 | for (int ii = 0; ii < tmpPolygons.get(i).size; ii++)
50 | polygons[i][ii] = new Vector2(tmpPolygons.get(i).get(ii));
51 | }
52 | }
53 | break;
54 | }
55 |
56 | if (polygons != null) polygons = sliceForMax8Vertices(polygons);
57 | return polygons;
58 | }
59 |
60 | private static Vector2[][] sliceForMax8Vertices(Vector2[][] polygons) {
61 | for (int i = 0; i < polygons.length; i++) {
62 | Vector2[] poly = polygons[i];
63 | if (poly.length > 8) {
64 | int limit = poly.length < 15 ? poly.length / 2 + 1 : 8;
65 | Vector2[] newPoly1 = new Vector2[limit];
66 | Vector2[] newPoly2 = new Vector2[poly.length - limit + 2];
67 | System.arraycopy(poly, 0, newPoly1, 0, limit);
68 | System.arraycopy(poly, limit - 1, newPoly2, 0, poly.length - limit + 1);
69 | newPoly2[newPoly2.length - 1] = poly[0].cpy();
70 |
71 | Vector2[][] newPolys = new Vector2[polygons.length + 1][];
72 | if (i > 0) {
73 | System.arraycopy(polygons, 0, newPolys, 0, i);
74 | }
75 | if (i < polygons.length - 1) {
76 | System.arraycopy(polygons, i + 1, newPolys, i + 2, polygons.length - i - 1);
77 | }
78 | newPolys[i] = newPoly1;
79 | newPolys[i + 1] = newPoly2;
80 | polygons = newPolys;
81 |
82 | i -= 1;
83 | }
84 | }
85 | return polygons;
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/editor/src/main/java/aurelienribon/bodyeditor/maths/Tracer.java:
--------------------------------------------------------------------------------
1 | package aurelienribon.bodyeditor.maths;
2 |
3 | import aurelienribon.bodyeditor.maths.trace.TextureConverter;
4 | import aurelienribon.utils.gdx.TextureUtils;
5 | import com.badlogic.gdx.graphics.Pixmap;
6 | import com.badlogic.gdx.graphics.Pixmap.Blending;
7 | import com.badlogic.gdx.graphics.g2d.TextureRegion;
8 | import com.badlogic.gdx.math.Vector2;
9 | import com.badlogic.gdx.utils.Array;
10 |
11 | /**
12 | * @author Aurelien Ribon | http://www.aurelienribon.com/
13 | */
14 | public class Tracer {
15 | public static Vector2[][] trace(String path, float hullTolerance, int alphaTolerance, boolean multiPartDetection, boolean holeDetection) {
16 | Pixmap pixmap = TextureUtils.getPOTPixmap(path);
17 | Blending blending = pixmap.getBlending();
18 | pixmap.setBlending(Blending.None);
19 |
20 | int w = pixmap.getWidth();
21 | int h = pixmap.getHeight();
22 |
23 | int size = w * h;
24 | int[] array = new int[size];
25 |
26 | for (int y = 0; y < h; y++) {
27 | for (int x = 0; x < w; x++) {
28 | int color = pixmap.getPixel(x, y);
29 | array[x + y * w] = color;
30 | }
31 | }
32 |
33 | pixmap.setBlending(blending);
34 | pixmap.dispose();
35 |
36 | Array> outlines;
37 | try {
38 | outlines = TextureConverter.createPolygon(array, w, h, hullTolerance, alphaTolerance, multiPartDetection, holeDetection);
39 | } catch (Exception e) {
40 | return null;
41 | }
42 |
43 | TextureRegion region = TextureUtils.getPOTTexture(path);
44 | float tw = region.getRegionWidth();
45 | float th = region.getRegionHeight();
46 |
47 | Vector2[][] polygons = new Vector2[outlines.size][];
48 |
49 | for (int i = 0; i < outlines.size; i++) {
50 | Array outline = outlines.get(i);
51 | polygons[i] = new Vector2[outline.size];
52 | for (int ii = 0; ii < outline.size; ii++) {
53 | polygons[i][ii] = outline.get(ii);
54 | polygons[i][ii].x /= tw;
55 | polygons[i][ii].y /= tw;
56 | polygons[i][ii].y = 1 * th / tw - polygons[i][ii].y;
57 | }
58 | }
59 |
60 | return polygons;
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/editor/src/main/java/aurelienribon/bodyeditor/maths/earclipping/ewjordan/EwjordanDecomposer.java:
--------------------------------------------------------------------------------
1 | package aurelienribon.bodyeditor.maths.earclipping.ewjordan;
2 |
3 | import com.badlogic.gdx.math.Vector2;
4 |
5 | /**
6 | * Original code from EwJordan (http://www.ewjordan.com/earClip/)
7 | */
8 | public class EwjordanDecomposer {
9 | public static Vector2[][] decompose(Vector2[] points) {
10 | int vNum = points.length;
11 | float[] xv = new float[vNum];
12 | float[] yv = new float[vNum];
13 |
14 | for (int i = 0; i < vNum; i++) {
15 | xv[i] = points[i].x;
16 | yv[i] = points[i].y;
17 | }
18 |
19 | Triangle[] tempTriangles = triangulatePolygon(xv, yv, vNum);
20 | Polygon[] tempPolygons = polygonizeTriangles(tempTriangles);
21 |
22 | if (tempPolygons == null)
23 | return null;
24 |
25 | Vector2[][] polygons = new Vector2[tempPolygons.length][];
26 | for (int i = 0; i < tempPolygons.length; i++) {
27 | polygons[i] = new Vector2[tempPolygons[i].nVertices];
28 | for (int ii = 0; ii < tempPolygons[i].nVertices; ii++)
29 | polygons[i][ii] = new Vector2(tempPolygons[i].x[ii], tempPolygons[i].y[ii]);
30 | }
31 |
32 | return polygons;
33 | }
34 |
35 | // -------------------------------------------------------------------------
36 |
37 | private static Triangle[] triangulatePolygon(float[] xv, float[] yv, int vNum) {
38 | if (vNum < 3)
39 | return null;
40 |
41 | Triangle[] buffer = new Triangle[vNum];
42 | int bufferSize = 0;
43 | float[] xrem = new float[vNum];
44 | float[] yrem = new float[vNum];
45 | for (int i = 0; i < vNum; ++i) {
46 | xrem[i] = xv[i];
47 | yrem[i] = yv[i];
48 | }
49 |
50 | while (vNum > 3) {
51 | int earIndex = -1;
52 | for (int i = 0; i < vNum; ++i) {
53 | if (isEar(i, xrem, yrem)) {
54 | earIndex = i;
55 | break;
56 | }
57 | }
58 |
59 | if (earIndex == -1)
60 | return null;
61 |
62 | --vNum;
63 | float[] newx = new float[vNum];
64 | float[] newy = new float[vNum];
65 | int currDest = 0;
66 | for (int i = 0; i < vNum; ++i) {
67 | if (currDest == earIndex) {
68 | ++currDest;
69 | }
70 | newx[i] = xrem[currDest];
71 | newy[i] = yrem[currDest];
72 | ++currDest;
73 | }
74 |
75 | int under = (earIndex == 0) ? (xrem.length - 1) : (earIndex - 1);
76 | int over = (earIndex == xrem.length - 1) ? 0 : (earIndex + 1);
77 |
78 | Triangle toAdd = new Triangle(xrem[earIndex], yrem[earIndex], xrem[over], yrem[over], xrem[under], yrem[under]);
79 | buffer[bufferSize] = toAdd;
80 | ++bufferSize;
81 |
82 | xrem = newx;
83 | yrem = newy;
84 | }
85 | Triangle toAdd = new Triangle(xrem[1], yrem[1], xrem[2], yrem[2], xrem[0], yrem[0]);
86 | buffer[bufferSize] = toAdd;
87 | ++bufferSize;
88 |
89 | Triangle[] res = new Triangle[bufferSize];
90 | System.arraycopy(buffer, 0, res, 0, bufferSize);
91 | return res;
92 | }
93 |
94 | private static Polygon[] polygonizeTriangles(Triangle[] triangulated) {
95 | Polygon[] polys;
96 | int polyIndex = 0;
97 |
98 | if (triangulated == null)
99 | return null;
100 |
101 | polys = new Polygon[triangulated.length];
102 | boolean[] covered = new boolean[triangulated.length];
103 | for (int i = 0; i < triangulated.length; i++)
104 | covered[i] = false;
105 |
106 | boolean notDone = true;
107 |
108 | while (notDone) {
109 | int currTri = -1;
110 | for (int i = 0; i < triangulated.length; i++) {
111 | if (!covered[i]) {
112 | currTri = i;
113 | break;
114 | }
115 | }
116 |
117 | if (currTri == -1) {
118 | notDone = false;
119 | } else {
120 | Polygon poly = new Polygon(triangulated[currTri]);
121 | covered[currTri] = true;
122 | for (int i = 0; i < triangulated.length; i++) {
123 | if (covered[i])
124 | continue;
125 |
126 | Polygon newP = poly.add(triangulated[i]);
127 | if (newP == null)
128 | continue;
129 |
130 | if (newP.isConvex()) {
131 | poly = newP;
132 | covered[i] = true;
133 | }
134 | }
135 | polys[polyIndex] = poly;
136 | polyIndex++;
137 | }
138 | }
139 |
140 | Polygon[] ret = new Polygon[polyIndex];
141 | System.arraycopy(polys, 0, ret, 0, polyIndex);
142 | return ret;
143 | }
144 |
145 | private static boolean isEar(int i, float[] xv, float[] yv) {
146 | float dx0, dy0, dx1, dy1;
147 | dx0 = dy0 = dx1 = dy1 = 0;
148 |
149 | if (i >= xv.length || i < 0 || xv.length < 3)
150 | return false;
151 |
152 | int upper = i + 1;
153 | int lower = i - 1;
154 |
155 | if (i == 0) {
156 | dx0 = xv[0] - xv[xv.length - 1];
157 | dy0 = yv[0] - yv[yv.length - 1];
158 | dx1 = xv[1] - xv[0];
159 | dy1 = yv[1] - yv[0];
160 | lower = xv.length - 1;
161 | } else if (i == xv.length - 1) {
162 | dx0 = xv[i] - xv[i - 1];
163 | dy0 = yv[i] - yv[i - 1];
164 | dx1 = xv[0] - xv[i];
165 | dy1 = yv[0] - yv[i];
166 | upper = 0;
167 | } else {
168 | dx0 = xv[i] - xv[i - 1];
169 | dy0 = yv[i] - yv[i - 1];
170 | dx1 = xv[i + 1] - xv[i];
171 | dy1 = yv[i + 1] - yv[i];
172 | }
173 |
174 | float cross = dx0 * dy1 - dx1 * dy0;
175 | if (cross > 0)
176 | return false;
177 |
178 | Triangle myTri = new Triangle(xv[i], yv[i], xv[upper], yv[upper], xv[lower], yv[lower]);
179 | for (int j = 0; j < xv.length; ++j) {
180 | if (j == i || j == lower || j == upper)
181 | continue;
182 | if (myTri.isInside(xv[j], yv[j]))
183 | return false;
184 | }
185 |
186 | return true;
187 | }
188 | }
189 |
--------------------------------------------------------------------------------
/editor/src/main/java/aurelienribon/bodyeditor/maths/earclipping/ewjordan/Polygon.java:
--------------------------------------------------------------------------------
1 | package aurelienribon.bodyeditor.maths.earclipping.ewjordan;
2 |
3 | /**
4 | * @author EwJordan (http://www.ewjordan.com/earClip/)
5 | */
6 | public class Polygon {
7 | public float[] x;
8 | public float[] y;
9 | public int nVertices;
10 |
11 | public Polygon(float[] _x, float[] _y) {
12 | nVertices = _x.length;
13 | x = new float[nVertices];
14 | y = new float[nVertices];
15 | for (int i = 0; i < nVertices; ++i) {
16 | x[i] = _x[i];
17 | y[i] = _y[i];
18 | }
19 | }
20 |
21 | public Polygon(Triangle t) {
22 | this(t.x, t.y);
23 | }
24 |
25 | public void set(Polygon p) {
26 | nVertices = p.nVertices;
27 | x = new float[nVertices];
28 | y = new float[nVertices];
29 | for (int i = 0; i < nVertices; ++i) {
30 | x[i] = p.x[i];
31 | y[i] = p.y[i];
32 | }
33 | }
34 |
35 | public boolean isConvex() {
36 | boolean isPositive = false;
37 | for (int i = 0; i < nVertices; ++i) {
38 | int lower = (i == 0) ? (nVertices - 1) : (i - 1);
39 | int middle = i;
40 | int upper = (i == nVertices - 1) ? (0) : (i + 1);
41 | float dx0 = x[middle] - x[lower];
42 | float dy0 = y[middle] - y[lower];
43 | float dx1 = x[upper] - x[middle];
44 | float dy1 = y[upper] - y[middle];
45 | float cross = dx0 * dy1 - dx1 * dy0;
46 | //Cross product should have same sign
47 | //for each vertex if poly is convex.
48 | boolean newIsP = (cross > 0) ? true : false;
49 | if (i == 0) {
50 | isPositive = newIsP;
51 | } else if (isPositive != newIsP) {
52 | return false;
53 | }
54 | }
55 | return true;
56 | }
57 |
58 | /*
59 | * Tries to add a triangle to the polygon.
60 | * Returns null if it can't connect properly.
61 | * Assumes bitwise equality of join vertices.
62 | */
63 | public Polygon add(Triangle t) {
64 | //First, find vertices that connect
65 | int firstP = -1;
66 | int firstT = -1;
67 | int secondP = -1;
68 | int secondT = -1;
69 | for (int i = 0; i < nVertices; i++) {
70 | if (t.x[0] == x[i] && t.y[0] == y[i]) {
71 | if (firstP == -1) {
72 | firstP = i;
73 | firstT = 0;
74 | } else {
75 | secondP = i;
76 | secondT = 0;
77 | }
78 | } else if (t.x[1] == x[i] && t.y[1] == y[i]) {
79 | if (firstP == -1) {
80 | firstP = i;
81 | firstT = 1;
82 | } else {
83 | secondP = i;
84 | secondT = 1;
85 | }
86 | } else if (t.x[2] == x[i] && t.y[2] == y[i]) {
87 | if (firstP == -1) {
88 | firstP = i;
89 | firstT = 2;
90 | } else {
91 | secondP = i;
92 | secondT = 2;
93 | }
94 | }
95 | }
96 | //Fix ordering if first should be last vertex of poly
97 | if (firstP == 0 && secondP == nVertices - 1) {
98 | firstP = nVertices - 1;
99 | secondP = 0;
100 | }
101 |
102 | //Didn't find it
103 | if (secondP == -1) {
104 | return null;
105 | }
106 |
107 | //Find tip index on triangle
108 | int tipT = 0;
109 | if (tipT == firstT || tipT == secondT) {
110 | tipT = 1;
111 | }
112 | if (tipT == firstT || tipT == secondT) {
113 | tipT = 2;
114 | }
115 |
116 | float[] newx = new float[nVertices + 1];
117 | float[] newy = new float[nVertices + 1];
118 | int currOut = 0;
119 | for (int i = 0; i < nVertices; i++) {
120 | newx[currOut] = x[i];
121 | newy[currOut] = y[i];
122 | if (i == firstP) {
123 | ++currOut;
124 | newx[currOut] = t.x[tipT];
125 | newy[currOut] = t.y[tipT];
126 | }
127 | ++currOut;
128 | }
129 | return new Polygon(newx, newy);
130 | }
131 | }
132 |
--------------------------------------------------------------------------------
/editor/src/main/java/aurelienribon/bodyeditor/maths/earclipping/ewjordan/Triangle.java:
--------------------------------------------------------------------------------
1 | package aurelienribon.bodyeditor.maths.earclipping.ewjordan;
2 |
3 | /**
4 | * @author EwJordan (http://www.ewjordan.com/earClip/)
5 | */
6 | public class Triangle {
7 | public float[] x;
8 | public float[] y;
9 |
10 | public Triangle(float x1, float y1, float x2, float y2, float x3, float y3) {
11 | x = new float[3];
12 | y = new float[3];
13 | float dx1 = x2 - x1;
14 | float dx2 = x3 - x1;
15 | float dy1 = y2 - y1;
16 | float dy2 = y3 - y1;
17 | float cross = dx1 * dy2 - dx2 * dy1;
18 | boolean ccw = (cross > 0);
19 | if (ccw) {
20 | x[0] = x1;
21 | x[1] = x2;
22 | x[2] = x3;
23 | y[0] = y1;
24 | y[1] = y2;
25 | y[2] = y3;
26 | } else {
27 | x[0] = x1;
28 | x[1] = x3;
29 | x[2] = x2;
30 | y[0] = y1;
31 | y[1] = y3;
32 | y[2] = y2;
33 | }
34 | }
35 |
36 | public boolean isInside(float _x, float _y) {
37 | float vx2 = _x - x[0];
38 | float vy2 = _y - y[0];
39 | float vx1 = x[1] - x[0];
40 | float vy1 = y[1] - y[0];
41 | float vx0 = x[2] - x[0];
42 | float vy0 = y[2] - y[0];
43 |
44 | float dot00 = vx0 * vx0 + vy0 * vy0;
45 | float dot01 = vx0 * vx1 + vy0 * vy1;
46 | float dot02 = vx0 * vx2 + vy0 * vy2;
47 | float dot11 = vx1 * vx1 + vy1 * vy1;
48 | float dot12 = vx1 * vx2 + vy1 * vy2;
49 | float invDenom = 1f / (dot00 * dot11 - dot01 * dot01);
50 | float u = (dot11 * dot02 - dot01 * dot12) * invDenom;
51 | float v = (dot00 * dot12 - dot01 * dot02) * invDenom;
52 |
53 | return ((u > 0) && (v > 0) && (u + v < 1));
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/editor/src/main/java/aurelienribon/bodyeditor/models/CircleModel.java:
--------------------------------------------------------------------------------
1 | package aurelienribon.bodyeditor.models;
2 |
3 | import com.badlogic.gdx.math.Vector2;
4 |
5 | /**
6 | * @author Aurelien Ribon | http://www.aurelienribon.com/
7 | */
8 | public class CircleModel {
9 | public final Vector2 center = new Vector2();
10 | public float radius;
11 |
12 | public CircleModel() {
13 | }
14 |
15 | public CircleModel(Vector2 center, float radius) {
16 | this.center.set(center);
17 | this.radius = radius;
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/editor/src/main/java/aurelienribon/bodyeditor/models/DynamicObjectModel.java:
--------------------------------------------------------------------------------
1 | package aurelienribon.bodyeditor.models;
2 |
3 | import aurelienribon.utils.notifications.ChangeableObject;
4 |
5 | import java.util.ArrayList;
6 | import java.util.List;
7 |
8 | /**
9 | * @author Aurelien Ribon | http://www.aurelienribon.com/
10 | */
11 | public class DynamicObjectModel extends ChangeableObject {
12 | private final List tuples = new ArrayList();
13 | private String name;
14 |
15 | public void addTuple(BodyTuple tuple) {
16 | tuples.add(tuple);
17 | }
18 |
19 | public void removeTuple(BodyTuple tuple) {
20 | tuples.remove(tuple);
21 | }
22 |
23 | public List getTuples() {
24 | return tuples;
25 | }
26 |
27 | public void setName(String name) {
28 | assert name != null;
29 | this.name = name;
30 | }
31 |
32 | public String getName() {
33 | return name;
34 | }
35 |
36 | public static class BodyAttributes {
37 | public float x;
38 | public float y;
39 | public float rotation;
40 | public float scale;
41 | }
42 |
43 | public static class BodyTuple {
44 | public final RigidBodyModel model;
45 | public final BodyAttributes attrs;
46 |
47 | public BodyTuple(RigidBodyModel model) {
48 | this.model = model;
49 | this.attrs = new BodyAttributes();
50 | }
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/editor/src/main/java/aurelienribon/bodyeditor/models/PolygonModel.java:
--------------------------------------------------------------------------------
1 | package aurelienribon.bodyeditor.models;
2 |
3 | import com.badlogic.gdx.math.Vector2;
4 |
5 | import java.util.ArrayList;
6 | import java.util.Arrays;
7 | import java.util.List;
8 |
9 | /**
10 | * @author Aurelien Ribon | http://www.aurelienribon.com/
11 | */
12 | public class PolygonModel {
13 | public final List vertices = new ArrayList();
14 |
15 | public PolygonModel() {
16 | }
17 |
18 | public PolygonModel(Vector2[] vertices) {
19 | this.vertices.addAll(Arrays.asList(vertices));
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/editor/src/main/java/aurelienribon/bodyeditor/models/RigidBodyModel.java:
--------------------------------------------------------------------------------
1 | package aurelienribon.bodyeditor.models;
2 |
3 | import aurelienribon.bodyeditor.Ctx;
4 | import aurelienribon.bodyeditor.Settings;
5 | import aurelienribon.bodyeditor.maths.Clipper;
6 | import aurelienribon.utils.notifications.ChangeableObject;
7 | import com.badlogic.gdx.math.Vector2;
8 |
9 | import java.util.ArrayList;
10 | import java.util.List;
11 |
12 | /**
13 | * @author Aurelien Ribon | http://www.aurelienribon.com/
14 | */
15 | public class RigidBodyModel extends ChangeableObject {
16 | public static final String PROP_NAME = "name";
17 | public static final String PROP_IMAGEPATH = "imagePath";
18 | public static final String PROP_PHYSICS = "physics";
19 |
20 | private final Vector2 origin = new Vector2(0, 0);
21 | private final List shapes = new ArrayList();
22 | private final List polygons = new ArrayList();
23 | private final List circles = new ArrayList();
24 | private String name = "unamed";
25 | private String imagePath;
26 | private boolean isImagePathValid = true;
27 |
28 | public Vector2 getOrigin() {
29 | return origin;
30 | }
31 |
32 | public List getShapes() {
33 | return shapes;
34 | }
35 |
36 | public List getPolygons() {
37 | return polygons;
38 | }
39 |
40 | public List getCircles() {
41 | return circles;
42 | }
43 |
44 | public void setName(String name) {
45 | assert name != null;
46 | this.name = name;
47 | firePropertyChanged(PROP_NAME);
48 | }
49 |
50 | public String getName() {
51 | return name;
52 | }
53 |
54 | public void setImagePath(String imagePath) {
55 | this.imagePath = imagePath;
56 | this.isImagePathValid = imagePath == null ? true : Ctx.io.getImageFile(imagePath).isFile();
57 | firePropertyChanged(PROP_IMAGEPATH);
58 | }
59 |
60 | public String getImagePath() {
61 | return imagePath;
62 | }
63 |
64 | public boolean isImagePathValid() {
65 | return isImagePathValid;
66 | }
67 |
68 | public void clear() {
69 | shapes.clear();
70 | polygons.clear();
71 | circles.clear();
72 | firePropertyChanged(PROP_PHYSICS);
73 | }
74 |
75 | public void clearPhysics() {
76 | polygons.clear();
77 | circles.clear();
78 | firePropertyChanged(PROP_PHYSICS);
79 | }
80 |
81 | public void computePhysics() {
82 | polygons.clear();
83 | circles.clear();
84 |
85 | for (ShapeModel shape : shapes) {
86 | if (!shape.isClosed()) continue;
87 |
88 | if (shape.getType() == ShapeModel.Type.POLYGON) {
89 | Vector2[] vertices = shape.getVertices().toArray(new Vector2[0]);
90 | Vector2[][] polys = Clipper.polygonize(Settings.polygonizer, vertices);
91 | if (polys != null) for (Vector2[] poly : polys) polygons.add(new PolygonModel(poly));
92 |
93 | }
94 | if (shape.getType() == ShapeModel.Type.CIRCLE) {
95 | Vector2 center = shape.getVertices().get(0);
96 | float radius = Math.abs(shape.getVertices().get(1).cpy().sub(center).len());
97 | circles.add(new CircleModel(center, radius));
98 | }
99 | }
100 |
101 | firePropertyChanged(PROP_PHYSICS);
102 | }
103 | }
104 |
--------------------------------------------------------------------------------
/editor/src/main/java/aurelienribon/bodyeditor/models/ShapeModel.java:
--------------------------------------------------------------------------------
1 | package aurelienribon.bodyeditor.models;
2 |
3 | import com.badlogic.gdx.math.Vector2;
4 |
5 | import java.util.ArrayList;
6 | import java.util.List;
7 |
8 | /**
9 | * @author Aurelien Ribon | http://www.aurelienribon.com/
10 | */
11 | public class ShapeModel {
12 | public enum Type {POLYGON, CIRCLE}
13 |
14 | private final List vertices = new ArrayList();
15 | private final Type type;
16 | private boolean isClosed = false;
17 |
18 | public ShapeModel(Type type) {
19 | this.type = type;
20 | }
21 |
22 | public List getVertices() {
23 | return vertices;
24 | }
25 |
26 | public Type getType() {
27 | return type;
28 | }
29 |
30 | public void close() {
31 | isClosed = true;
32 | }
33 |
34 | public boolean isClosed() {
35 | return isClosed;
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/editor/src/main/java/aurelienribon/bodyeditor/ui/DynamicObjectsPanel.form:
--------------------------------------------------------------------------------
1 |
2 |
3 |
160 |
--------------------------------------------------------------------------------
/editor/src/main/java/aurelienribon/bodyeditor/ui/HelpDialog.form:
--------------------------------------------------------------------------------
1 |
2 |
3 |
104 |
--------------------------------------------------------------------------------
/editor/src/main/java/aurelienribon/bodyeditor/ui/HelpDialog.java:
--------------------------------------------------------------------------------
1 | package aurelienribon.bodyeditor.ui;
2 |
3 | import javax.swing.*;
4 |
5 | /**
6 | * @author Aurelien Ribon | http://www.aurelienribon.com/
7 | */
8 | public class HelpDialog extends javax.swing.JDialog {
9 | public HelpDialog(JFrame frame) {
10 | super(frame, true);
11 | initComponents();
12 | }
13 |
14 | @SuppressWarnings("unchecked")
15 | // //GEN-BEGIN:initComponents
16 | private void initComponents() {
17 |
18 | jPanel1 = new javax.swing.JPanel();
19 | jPanel4 = new javax.swing.JPanel();
20 | jLabel2 = new javax.swing.JLabel();
21 |
22 | setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
23 | setTitle("Physics Body Editor - Help");
24 |
25 | jLabel2.setText(" Tutorial 1. Set the output file, 2. Add some images to the list, 3. Select an image and define its collision shape(s) Note: if your output file extension is \".xml\" or \".json\", then saves will be done in XML or JSON format. For any other extension, saves will be done in binary. Don't forget to save your work before closing the application! ;-) Controls Create a shape with <ctrl>+<left clics> Edit a shape by dragging its vertices by holding <left mouse button> Test the collisions by holding <shift> and dragging the <left mouse button> Zoom with <mouse scroll> Pan by dragging the <right mouse button> Mac OS users: if <ctrl> or <shift> is not recognized, you can also hold <C> or <S> (respectively). Hints If you need to insert a vertex between two others, select these two vertices, and clic on the \"insert vertices\" button. A new vertex will be inserted between each pair of selected vertices.");
26 | jLabel2.setVerticalAlignment(javax.swing.SwingConstants.TOP);
27 |
28 | javax.swing.GroupLayout jPanel4Layout = new javax.swing.GroupLayout(jPanel4);
29 | jPanel4.setLayout(jPanel4Layout);
30 | jPanel4Layout.setHorizontalGroup(
31 | jPanel4Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
32 | .addGroup(jPanel4Layout.createSequentialGroup()
33 | .addContainerGap()
34 | .addComponent(jLabel2, javax.swing.GroupLayout.DEFAULT_SIZE, 555, Short.MAX_VALUE)
35 | .addContainerGap())
36 | );
37 | jPanel4Layout.setVerticalGroup(
38 | jPanel4Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
39 | .addGroup(jPanel4Layout.createSequentialGroup()
40 | .addContainerGap()
41 | .addComponent(jLabel2, javax.swing.GroupLayout.DEFAULT_SIZE, 316, Short.MAX_VALUE)
42 | .addContainerGap())
43 | );
44 |
45 | javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1);
46 | jPanel1.setLayout(jPanel1Layout);
47 | jPanel1Layout.setHorizontalGroup(
48 | jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
49 | .addGroup(jPanel1Layout.createSequentialGroup()
50 | .addContainerGap()
51 | .addComponent(jPanel4, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
52 | .addContainerGap())
53 | );
54 | jPanel1Layout.setVerticalGroup(
55 | jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
56 | .addGroup(jPanel1Layout.createSequentialGroup()
57 | .addContainerGap()
58 | .addComponent(jPanel4, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
59 | .addContainerGap())
60 | );
61 |
62 | getContentPane().add(jPanel1, java.awt.BorderLayout.CENTER);
63 |
64 | pack();
65 | }// //GEN-END:initComponents
66 |
67 | // Variables declaration - do not modify//GEN-BEGIN:variables
68 | private javax.swing.JLabel jLabel2;
69 | private javax.swing.JPanel jPanel1;
70 | private javax.swing.JPanel jPanel4;
71 | // End of variables declaration//GEN-END:variables
72 | }
73 |
--------------------------------------------------------------------------------
/editor/src/main/java/aurelienribon/bodyeditor/ui/Main.java:
--------------------------------------------------------------------------------
1 | package aurelienribon.bodyeditor.ui;
2 |
3 | import aurelienribon.bodyeditor.Ctx;
4 | import aurelienribon.bodyeditor.canvas.Canvas;
5 | import com.badlogic.gdx.backends.lwjgl.LwjglApplicationConfiguration;
6 | import com.badlogic.gdx.backends.lwjgl.LwjglCanvas;
7 | import org.json.JSONException;
8 |
9 | import javax.swing.*;
10 | import java.awt.*;
11 | import java.io.File;
12 | import java.io.IOException;
13 |
14 | /**
15 | * @author Aurelien Ribon | http://www.aurelienribon.com/
16 | */
17 | public class Main {
18 | public static void main(final String[] args) {
19 | SwingUtilities.invokeLater(new Runnable() {
20 | @Override
21 | public void run() {
22 | try {
23 | UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
24 | } catch (ClassNotFoundException ex) {
25 | } catch (InstantiationException ex) {
26 | } catch (IllegalAccessException ex) {
27 | } catch (UnsupportedLookAndFeelException ex) {
28 | }
29 |
30 | LwjglApplicationConfiguration config = new LwjglApplicationConfiguration();
31 | config.useGL30 = true;
32 | LwjglCanvas glCanvas = new LwjglCanvas(new Canvas(), config);
33 | MainWindow mw = Ctx.window;
34 |
35 | Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
36 | mw.setSize(
37 | Math.min(1150, screenSize.width - 100),
38 | Math.min(800, screenSize.height - 100)
39 | );
40 |
41 | mw.setCanvas(glCanvas.getCanvas());
42 | mw.setLocationRelativeTo(null);
43 | mw.setVisible(true);
44 |
45 | parseArgs(args);
46 | }
47 | });
48 | }
49 |
50 | private static void parseArgs(String[] args) {
51 | for (int i = 1; i < args.length; i++) {
52 | if (args[i - 1].equals("-f")) {
53 | try {
54 | File file = new File(args[i]).getCanonicalFile();
55 | Ctx.io.setProjectFile(file);
56 | Ctx.io.importFromFile();
57 | } catch (IOException ex) {
58 | } catch (JSONException ex) {
59 | }
60 | }
61 | }
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/editor/src/main/java/aurelienribon/bodyeditor/ui/ProjectPanel.form:
--------------------------------------------------------------------------------
1 |
2 |
3 |
150 |
--------------------------------------------------------------------------------
/editor/src/main/java/aurelienribon/bodyeditor/ui/RepairImagePathsDialog.form:
--------------------------------------------------------------------------------
1 |
2 |
3 |
112 |
--------------------------------------------------------------------------------
/editor/src/main/java/aurelienribon/bodyeditor/ui/RepairImagePathsDialog.java:
--------------------------------------------------------------------------------
1 | package aurelienribon.bodyeditor.ui;
2 |
3 | import aurelienribon.Res;
4 | import aurelienribon.bodyeditor.Ctx;
5 | import aurelienribon.bodyeditor.models.RigidBodyModel;
6 | import aurelienribon.ui.components.PaintedPanel;
7 | import aurelienribon.ui.css.Style;
8 | import org.apache.commons.io.FilenameUtils;
9 |
10 | import java.awt.event.ActionEvent;
11 | import java.awt.event.ActionListener;
12 | import java.io.File;
13 |
14 | /**
15 | * @author Aurelien Ribon | http://www.aurelienribon.com/
16 | */
17 | public class RepairImagePathsDialog extends javax.swing.JDialog {
18 | private String commonPath;
19 |
20 | public RepairImagePathsDialog(javax.swing.JFrame parent) {
21 | super(parent, true);
22 |
23 | setContentPane(new PaintedPanel());
24 | initComponents();
25 |
26 | Style.registerCssClasses(getContentPane(), ".rootPanel", ".configPanel");
27 | Style.apply(getContentPane(), new Style(Res.getUrl("css/style.css")));
28 |
29 | okBtn.addActionListener(new ActionListener() {
30 | @Override
31 | public void actionPerformed(ActionEvent e) {
32 | apply();
33 | dispose();
34 | }
35 | });
36 |
37 | cancelBtn.addActionListener(new ActionListener() {
38 | @Override
39 | public void actionPerformed(ActionEvent e) {
40 | dispose();
41 | }
42 | });
43 |
44 | init();
45 | }
46 |
47 | private void init() {
48 | for (RigidBodyModel model : Ctx.bodies.getModels()) {
49 | String path = FilenameUtils.getFullPathNoEndSeparator(model.getImagePath());
50 | path = FilenameUtils.normalizeNoEndSeparator(path, true);
51 |
52 | if (commonPath == null) {
53 | commonPath = path;
54 | } else {
55 | String[] pathParts = path.split("/");
56 | String[] commonPathParts = commonPath.split("/");
57 |
58 | for (int i = 0; i < pathParts.length && i < commonPathParts.length; i++) {
59 | if (!pathParts[i].equals(commonPathParts[i])) {
60 | commonPath = "";
61 | for (int ii = 0; ii < i; ii++)
62 | commonPath = FilenameUtils.concat(commonPath, commonPathParts[i]);
63 | break;
64 | }
65 | }
66 | }
67 | }
68 |
69 | commonPathField.setText(commonPath);
70 | commonPathField.selectAll();
71 | commonPathField.requestFocusInWindow();
72 | }
73 |
74 | private void apply() {
75 | String newCommonpath = commonPathField.getText();
76 |
77 | for (RigidBodyModel model : Ctx.bodies.getModels()) {
78 | String path = FilenameUtils.getFullPath(model.getImagePath());
79 | String name = FilenameUtils.getName(model.getImagePath());
80 | path = newCommonpath + path.substring(commonPath.length());
81 | model.setImagePath(new File(path, name).getPath());
82 | }
83 | }
84 |
85 | // -------------------------------------------------------------------------
86 | // Generated stuff
87 | // -------------------------------------------------------------------------
88 |
89 | @SuppressWarnings("unchecked")
90 | // //GEN-BEGIN:initComponents
91 | private void initComponents() {
92 |
93 | paintedPanel1 = new aurelienribon.ui.components.PaintedPanel();
94 | jLabel1 = new javax.swing.JLabel();
95 | commonPathField = new javax.swing.JTextField();
96 | okBtn = new javax.swing.JButton();
97 | cancelBtn = new javax.swing.JButton();
98 |
99 | setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
100 | setTitle("Repair image paths");
101 | setResizable(false);
102 |
103 | jLabel1.setText("Images common path: ");
104 |
105 | commonPathField.setText("...");
106 |
107 | okBtn.setText("Ok");
108 |
109 | cancelBtn.setText("Cancel");
110 |
111 | javax.swing.GroupLayout paintedPanel1Layout = new javax.swing.GroupLayout(paintedPanel1);
112 | paintedPanel1.setLayout(paintedPanel1Layout);
113 | paintedPanel1Layout.setHorizontalGroup(
114 | paintedPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
115 | .addGroup(paintedPanel1Layout.createSequentialGroup()
116 | .addContainerGap()
117 | .addGroup(paintedPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
118 | .addGroup(paintedPanel1Layout.createSequentialGroup()
119 | .addComponent(jLabel1)
120 | .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
121 | .addComponent(commonPathField))
122 | .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, paintedPanel1Layout.createSequentialGroup()
123 | .addGap(0, 178, Short.MAX_VALUE)
124 | .addComponent(okBtn)
125 | .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
126 | .addComponent(cancelBtn)))
127 | .addContainerGap())
128 | );
129 |
130 | paintedPanel1Layout.linkSize(javax.swing.SwingConstants.HORIZONTAL, new java.awt.Component[]{cancelBtn, okBtn});
131 |
132 | paintedPanel1Layout.setVerticalGroup(
133 | paintedPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
134 | .addGroup(paintedPanel1Layout.createSequentialGroup()
135 | .addContainerGap()
136 | .addGroup(paintedPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
137 | .addComponent(jLabel1)
138 | .addComponent(commonPathField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
139 | .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
140 | .addGroup(paintedPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
141 | .addComponent(okBtn)
142 | .addComponent(cancelBtn))
143 | .addContainerGap())
144 | );
145 |
146 | javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
147 | getContentPane().setLayout(layout);
148 | layout.setHorizontalGroup(
149 | layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
150 | .addGroup(layout.createSequentialGroup()
151 | .addContainerGap()
152 | .addComponent(paintedPanel1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
153 | .addContainerGap())
154 | );
155 | layout.setVerticalGroup(
156 | layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
157 | .addGroup(layout.createSequentialGroup()
158 | .addContainerGap()
159 | .addComponent(paintedPanel1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
160 | .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
161 | );
162 |
163 | pack();
164 | }// //GEN-END:initComponents
165 |
166 | // Variables declaration - do not modify//GEN-BEGIN:variables
167 | private javax.swing.JButton cancelBtn;
168 | private javax.swing.JTextField commonPathField;
169 | private javax.swing.JLabel jLabel1;
170 | private javax.swing.JButton okBtn;
171 | private aurelienribon.ui.components.PaintedPanel paintedPanel1;
172 | // End of variables declaration//GEN-END:variables
173 |
174 | }
175 |
--------------------------------------------------------------------------------
/editor/src/main/java/aurelienribon/bodyeditor/ui/RigidBodiesOptionsPanel.form:
--------------------------------------------------------------------------------
1 |
2 |
3 |
159 |
--------------------------------------------------------------------------------
/editor/src/main/java/aurelienribon/bodyeditor/utils/ShapeUtils.java:
--------------------------------------------------------------------------------
1 | package aurelienribon.bodyeditor.utils;
2 |
3 | import aurelienribon.bodyeditor.models.RigidBodyModel;
4 | import aurelienribon.bodyeditor.models.ShapeModel;
5 | import com.badlogic.gdx.math.Vector2;
6 |
7 | /**
8 | * @author Aurelien Ribon | http://www.aurelienribon.com/
9 | */
10 | public class ShapeUtils {
11 | public static ShapeModel getShape(RigidBodyModel model, Vector2 v) {
12 | for (ShapeModel shape : model.getShapes()) {
13 | if (shape.getVertices().contains(v)) return shape;
14 | }
15 |
16 | return null;
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/editor/src/main/java/aurelienribon/utils/gdx/PolygonUtils.java:
--------------------------------------------------------------------------------
1 | package aurelienribon.utils.gdx;
2 |
3 | import com.badlogic.gdx.math.Vector2;
4 |
5 | /**
6 | * @author Aurelien Ribon | http://www.aurelienribon.com/
7 | */
8 | public class PolygonUtils {
9 | public static float getPolygonSignedArea(Vector2[] points) {
10 | if (points.length < 3)
11 | return 0;
12 |
13 | float sum = 0;
14 | for (int i = 0; i < points.length; i++) {
15 | Vector2 p1 = points[i];
16 | Vector2 p2 = i != points.length - 1 ? points[i + 1] : points[0];
17 | sum += (p1.x * p2.y) - (p1.y * p2.x);
18 | }
19 | return 0.5f * sum;
20 | }
21 |
22 | public static float getPolygonArea(Vector2[] points) {
23 | return Math.abs(getPolygonSignedArea(points));
24 | }
25 |
26 | public static boolean isPolygonCCW(Vector2[] points) {
27 | return getPolygonSignedArea(points) > 0;
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/editor/src/main/java/aurelienribon/utils/gdx/SpriteUtils.java:
--------------------------------------------------------------------------------
1 | package aurelienribon.utils.gdx;
2 |
3 | import com.badlogic.gdx.graphics.g2d.Sprite;
4 |
5 | /**
6 | * @author Aurelien Ribon | http://www.aurelienribon.com/
7 | */
8 | public class SpriteUtils {
9 | public static boolean isOver(Sprite sp, float x, float y) {
10 | return sp.getX() <= x && x <= sp.getX() + sp.getWidth()
11 | && sp.getY() <= y && y <= sp.getY() + sp.getHeight();
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/editor/src/main/java/aurelienribon/utils/gdx/TextureUtils.java:
--------------------------------------------------------------------------------
1 | package aurelienribon.utils.gdx;
2 |
3 | import com.badlogic.gdx.Gdx;
4 | import com.badlogic.gdx.files.FileHandle;
5 | import com.badlogic.gdx.graphics.Pixmap;
6 | import com.badlogic.gdx.graphics.Texture;
7 | import com.badlogic.gdx.graphics.g2d.TextureRegion;
8 |
9 | /**
10 | * @author Aurelien Ribon | http://www.aurelienribon.com/
11 | */
12 | public class TextureUtils {
13 | private static final int[] potWidths = {1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 5096};
14 |
15 | // -------------------------------------------------------------------------
16 |
17 | public static TextureRegion getPOTTexture(String path) {
18 | if (path == null) return null;
19 |
20 | FileHandle file = Gdx.files.absolute(path);
21 | if (!file.exists()) return null;
22 |
23 | Pixmap pixmap = new Pixmap(file);
24 | int origW = pixmap.getWidth();
25 | int origH = pixmap.getHeight();
26 | int w = getNearestPOT(origW);
27 | int h = getNearestPOT(origH);
28 | int len = Math.max(w, h);
29 |
30 | Pixmap potPixmap = new Pixmap(len, len, pixmap.getFormat());
31 | potPixmap.drawPixmap(pixmap, 0, 0, 0, 0, origW, origH);
32 | pixmap.dispose();
33 |
34 | Texture texture = new Texture(potPixmap);
35 | texture.setFilter(Texture.TextureFilter.Linear, Texture.TextureFilter.Linear);
36 |
37 | return new TextureRegion(texture, 0, 0, origW, origH);
38 | }
39 |
40 | public static Pixmap getPOTPixmap(String path) {
41 | if (path == null) return null;
42 |
43 | FileHandle file = Gdx.files.absolute(path);
44 | if (!file.exists()) return null;
45 |
46 | Pixmap pixmap = new Pixmap(file);
47 | int origW = pixmap.getWidth();
48 | int origH = pixmap.getHeight();
49 | int w = getNearestPOT(origW);
50 | int h = getNearestPOT(origH);
51 | int len = Math.max(w, h);
52 |
53 | Pixmap potPixmap = new Pixmap(len, len, pixmap.getFormat());
54 | potPixmap.drawPixmap(pixmap, 0, 0, 0, 0, origW, origH);
55 | pixmap.dispose();
56 |
57 | return potPixmap;
58 | }
59 |
60 | // -------------------------------------------------------------------------
61 |
62 | private static int getNearestPOT(int d) {
63 | for (int i = 0; i < potWidths.length; i++)
64 | if (d <= potWidths[i])
65 | return potWidths[i];
66 | return -1;
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/editor/src/main/java/aurelienribon/utils/gdx/VectorUtils.java:
--------------------------------------------------------------------------------
1 | package aurelienribon.utils.gdx;
2 |
3 | import com.badlogic.gdx.math.Vector2;
4 |
5 | /**
6 | * @author Aurelien Ribon | http://www.aurelienribon.com/
7 | */
8 | public class VectorUtils {
9 | public static Vector2 mul(Vector2 v, float cx, float cy) {
10 | Vector2 ret = v.cpy();
11 | ret.x *= cx;
12 | ret.y *= cy;
13 | return ret;
14 | }
15 |
16 | public static Vector2[] mul(Vector2[] vs, float cx, float cy) {
17 | Vector2[] ret = new Vector2[vs.length];
18 | for (int i = 0; i < ret.length; i++) ret[i] = mul(vs[i], cx, cy);
19 | return ret;
20 | }
21 |
22 | public static Vector2[][] mul(Vector2[][] vss, float cx, float cy) {
23 | Vector2[][] ret = new Vector2[vss.length][];
24 | for (int i = 0; i < ret.length; i++) ret[i] = mul(vss[i], cx, cy);
25 | return ret;
26 | }
27 |
28 | // -------------------------------------------------------------------------
29 |
30 | public static Vector2 getCopy(Vector2 v) {
31 | if (v == null) return null;
32 | return v.cpy();
33 | }
34 |
35 | public static Vector2[] getCopy(Vector2[] vs) {
36 | if (vs == null) return null;
37 | Vector2[] ret = new Vector2[vs.length];
38 | for (int i = 0; i < ret.length; i++) ret[i] = getCopy(vs[i]);
39 | return ret;
40 | }
41 |
42 | public static Vector2[][] getCopy(Vector2[][] vss) {
43 | if (vss == null) return null;
44 | Vector2[][] ret = new Vector2[vss.length][];
45 | for (int i = 0; i < ret.length; i++) ret[i] = getCopy(vss[i]);
46 | return ret;
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/editor/src/main/java/aurelienribon/utils/io/FilenameHelper.java:
--------------------------------------------------------------------------------
1 | package aurelienribon.utils.io;
2 |
3 | import org.apache.commons.io.FilenameUtils;
4 |
5 | import java.io.File;
6 | import java.util.regex.Pattern;
7 |
8 | public class FilenameHelper {
9 | /**
10 | * Removes every '"' character before and after the path, if any.
11 | */
12 | public static String trim(String path) {
13 | while (path.startsWith("\"") && path.endsWith("\""))
14 | path = path.substring(1, path.length() - 1);
15 | return path;
16 | }
17 |
18 | /**
19 | * Gets the relative path from one file to another.
20 | */
21 | public static String getRelativePath(String targetPath, String basePath) {
22 | if (basePath == null || basePath.equals("")) return targetPath;
23 | if (targetPath == null || targetPath.equals("")) return "";
24 |
25 | String pathSeparator = File.separator;
26 |
27 | // Normalize the paths
28 | String normalizedTargetPath = FilenameUtils.normalizeNoEndSeparator(targetPath);
29 | String normalizedBasePath = FilenameUtils.normalizeNoEndSeparator(basePath);
30 |
31 | if (basePath.equals(targetPath)) return "";
32 |
33 | // Undo the changes to the separators made by normalization
34 | if (pathSeparator.equals("/")) {
35 | normalizedTargetPath = FilenameUtils.separatorsToUnix(normalizedTargetPath);
36 | normalizedBasePath = FilenameUtils.separatorsToUnix(normalizedBasePath);
37 |
38 | } else if (pathSeparator.equals("\\")) {
39 | normalizedTargetPath = FilenameUtils.separatorsToWindows(normalizedTargetPath);
40 | normalizedBasePath = FilenameUtils.separatorsToWindows(normalizedBasePath);
41 |
42 | } else {
43 | throw new IllegalArgumentException("Unrecognised dir separator '" + pathSeparator + "'");
44 | }
45 |
46 | String[] base = normalizedBasePath.split(Pattern.quote(pathSeparator));
47 | String[] target = normalizedTargetPath.split(Pattern.quote(pathSeparator));
48 |
49 | // First get all the common elements. Store them as a string,
50 | // and also count how many of them there are.
51 | StringBuilder common = new StringBuilder();
52 |
53 | int commonIndex = 0;
54 | while (commonIndex < target.length && commonIndex < base.length
55 | && target[commonIndex].equals(base[commonIndex])) {
56 | common.append(target[commonIndex]).append(pathSeparator);
57 | commonIndex++;
58 | }
59 |
60 | if (commonIndex == 0) {
61 | return targetPath;
62 | }
63 |
64 | // The number of directories we have to backtrack depends on whether the base is a file or a dir
65 | // For example, the relative path from
66 | //
67 | // /foo/bar/baz/gg/ff to /foo/bar/baz
68 | //
69 | // ".." if ff is a file
70 | // "../.." if ff is a directory
71 | //
72 | // The following is a heuristic to figure out if the base refers to a file or dir. It's not perfect, because
73 | // the resource referred to by this path may not actually exist, but it's the best I can do
74 | boolean baseIsFile = true;
75 |
76 | File baseResource = new File(normalizedBasePath);
77 |
78 | if (baseResource.exists()) {
79 | baseIsFile = baseResource.isFile();
80 |
81 | } else if (basePath.endsWith(pathSeparator)) {
82 | baseIsFile = false;
83 | }
84 |
85 | StringBuilder relative = new StringBuilder();
86 |
87 | if (base.length != commonIndex) {
88 | int numDirsUp = baseIsFile ? base.length - commonIndex - 1 : base.length - commonIndex;
89 |
90 | for (int i = 0; i < numDirsUp; i++) {
91 | relative.append("..").append(pathSeparator);
92 | }
93 | }
94 | relative.append(normalizedTargetPath.substring(common.length()));
95 | return relative.toString();
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/editor/src/main/java/aurelienribon/utils/io/HttpUtils.java:
--------------------------------------------------------------------------------
1 | package aurelienribon.utils.io;
2 |
3 | import java.io.BufferedInputStream;
4 | import java.io.IOException;
5 | import java.io.InputStream;
6 | import java.io.OutputStream;
7 | import java.net.HttpURLConnection;
8 | import java.net.URL;
9 |
10 | /**
11 | * @author Aurelien Ribon | http://www.aurelienribon.com/
12 | */
13 | public class HttpUtils {
14 | public static DownloadTask downloadAsync(URL input, OutputStream output, Callback callback) {
15 | final DownloadTask task = new DownloadTask(input, output, callback);
16 |
17 | new Thread(new Runnable() {
18 | @Override
19 | public void run() {
20 | task.download();
21 | }
22 | }).start();
23 |
24 | return task;
25 | }
26 |
27 | public static interface Callback {
28 | public void completed();
29 |
30 | public void canceled();
31 |
32 | public void error(IOException ex);
33 |
34 | public void updated(int length, int totalLength);
35 | }
36 |
37 | public static class DownloadTask {
38 | private final URL input;
39 | private final OutputStream output;
40 | private final Callback callback;
41 | private boolean run = true;
42 |
43 | public DownloadTask(URL input, OutputStream output, Callback callback) {
44 | this.input = input;
45 | this.output = output;
46 | this.callback = callback;
47 | }
48 |
49 | public void stop() {
50 | run = false;
51 | }
52 |
53 | public URL getInput() {
54 | return input;
55 | }
56 |
57 | public OutputStream getOutput() {
58 | return output;
59 | }
60 |
61 | public Callback getCallback() {
62 | return callback;
63 | }
64 |
65 | private void download() {
66 | OutputStream os = null;
67 | InputStream is = null;
68 | IOException ex = null;
69 |
70 | try {
71 | HttpURLConnection connection = (HttpURLConnection) input.openConnection();
72 | connection.setDoInput(true);
73 | connection.setDoOutput(false);
74 | connection.setUseCaches(true);
75 | connection.setConnectTimeout(3000);
76 | connection.connect();
77 |
78 | is = new BufferedInputStream(connection.getInputStream(), 4096);
79 | os = output;
80 |
81 | byte[] data = new byte[4096];
82 | int length = connection.getContentLength();
83 | int total = 0;
84 |
85 | int count;
86 | while (run && (count = is.read(data)) != -1) {
87 | total += count;
88 | os.write(data, 0, count);
89 | if (callback != null) callback.updated(total, length);
90 | }
91 |
92 | } catch (IOException ex1) {
93 | ex = ex1;
94 |
95 | } finally {
96 | if (os != null) try {
97 | os.flush();
98 | os.close();
99 | } catch (IOException ex1) {
100 | }
101 | if (is != null) try {
102 | is.close();
103 | } catch (IOException ex1) {
104 | }
105 |
106 | if (callback != null) {
107 | if (ex != null) callback.error(ex);
108 | else if (run == true) callback.completed();
109 | else callback.canceled();
110 | }
111 | }
112 | }
113 | }
114 | }
115 |
--------------------------------------------------------------------------------
/editor/src/main/java/aurelienribon/utils/notifications/AutoListModel.java:
--------------------------------------------------------------------------------
1 | package aurelienribon.utils.notifications;
2 |
3 | import javax.swing.*;
4 | import javax.swing.event.EventListenerList;
5 | import javax.swing.event.ListDataEvent;
6 | import javax.swing.event.ListDataListener;
7 | import java.util.List;
8 |
9 | /**
10 | * @author Aurelien Ribon | http://www.aurelienribon.com
11 | */
12 | public class AutoListModel implements ListModel {
13 | private final EventListenerList listeners = new EventListenerList();
14 | private final ObservableList model;
15 |
16 | public AutoListModel(ObservableList model) {
17 | this.model = model;
18 | model.addListChangedListener(modelListener);
19 |
20 | for (Object elem : model)
21 | if (elem instanceof Changeable)
22 | ((Changeable) elem).addChangeListener(elemChangeListener);
23 | }
24 |
25 | @Override
26 | public int getSize() {
27 | return model.size();
28 | }
29 |
30 | @Override
31 | public T getElementAt(int index) {
32 | return model.get(index);
33 | }
34 |
35 | @Override
36 | public void addListDataListener(ListDataListener l) {
37 | listeners.add(ListDataListener.class, l);
38 | }
39 |
40 | @Override
41 | public void removeListDataListener(ListDataListener l) {
42 | listeners.remove(ListDataListener.class, l);
43 | }
44 |
45 | public void forceRefresh() {
46 | ListDataEvent evt = new ListDataEvent(this, ListDataEvent.CONTENTS_CHANGED, 0, model.size() - 1);
47 | for (ListDataListener listener : listeners.getListeners(ListDataListener.class))
48 | listener.contentsChanged(evt);
49 | }
50 |
51 | // -------------------------------------------------------------------------
52 | // Helpers
53 | // -------------------------------------------------------------------------
54 |
55 | private void fireContentsChanged(int idx1, int idx2) {
56 | ListDataEvent evt = new ListDataEvent(this, ListDataEvent.CONTENTS_CHANGED, idx1, idx2);
57 | for (ListDataListener listener : listeners.getListeners(ListDataListener.class))
58 | listener.contentsChanged(evt);
59 | }
60 |
61 | // -------------------------------------------------------------------------
62 | // Listeners
63 | // -------------------------------------------------------------------------
64 |
65 | private final ObservableList.ListChangeListener modelListener = new ObservableList.ListChangeListener() {
66 | @Override
67 | public void changed(Object source, List added, List removed) {
68 | fireContentsChanged(0, model.size());
69 | for (T elem : added)
70 | if (elem instanceof Changeable) ((Changeable) elem).addChangeListener(elemChangeListener);
71 | for (T elem : removed)
72 | if (elem instanceof Changeable) ((Changeable) elem).removeChangeListener(elemChangeListener);
73 | }
74 | };
75 |
76 | private final ChangeListener elemChangeListener = new ChangeListener() {
77 | @Override
78 | public void propertyChanged(Object source, String propertyName) {
79 | int idx = model.indexOf(source);
80 | fireContentsChanged(idx, idx);
81 | }
82 | };
83 | }
84 |
--------------------------------------------------------------------------------
/editor/src/main/java/aurelienribon/utils/notifications/ChangeListener.java:
--------------------------------------------------------------------------------
1 | package aurelienribon.utils.notifications;
2 |
3 | /**
4 | * @author Aurelien Ribon | http://www.aurelienribon.com
5 | */
6 | public interface ChangeListener {
7 | public void propertyChanged(Object source, String propertyName);
8 | }
9 |
--------------------------------------------------------------------------------
/editor/src/main/java/aurelienribon/utils/notifications/Changeable.java:
--------------------------------------------------------------------------------
1 | package aurelienribon.utils.notifications;
2 |
3 | /**
4 | * @author Aurelien Ribon | http://www.aurelienribon.com
5 | */
6 | public interface Changeable {
7 | public void addChangeListener(ChangeListener l);
8 |
9 | public void removeChangeListener(ChangeListener l);
10 | }
11 |
--------------------------------------------------------------------------------
/editor/src/main/java/aurelienribon/utils/notifications/ChangeableObject.java:
--------------------------------------------------------------------------------
1 | package aurelienribon.utils.notifications;
2 |
3 | import java.util.ArrayList;
4 | import java.util.List;
5 |
6 | /**
7 | * @author Aurelien Ribon | http://www.aurelienribon.com
8 | */
9 | public class ChangeableObject implements Changeable {
10 | private final List changeListeners = new ArrayList(3);
11 |
12 | @Override
13 | public void addChangeListener(ChangeListener l) {
14 | changeListeners.add(l);
15 | }
16 |
17 | @Override
18 | public void removeChangeListener(ChangeListener l) {
19 | changeListeners.remove(l);
20 | }
21 |
22 | protected void firePropertyChanged(String propertyName) {
23 | for (ChangeListener listener : changeListeners)
24 | listener.propertyChanged(this, propertyName);
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/editor/src/main/java/aurelienribon/utils/notifications/ChangeableSupport.java:
--------------------------------------------------------------------------------
1 | package aurelienribon.utils.notifications;
2 |
3 | import java.util.ArrayList;
4 | import java.util.List;
5 |
6 | /**
7 | * @author Aurelien Ribon | http://www.aurelienribon.com
8 | */
9 | public class ChangeableSupport implements Changeable {
10 | private final List changeListeners = new ArrayList(3);
11 |
12 | @Override
13 | public void addChangeListener(ChangeListener l) {
14 | changeListeners.add(l);
15 | }
16 |
17 | @Override
18 | public void removeChangeListener(ChangeListener l) {
19 | changeListeners.remove(l);
20 | }
21 |
22 | public void firePropertyChanged(Object source, String propertyName) {
23 | for (ChangeListener listener : changeListeners)
24 | listener.propertyChanged(source, propertyName);
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/editor/src/main/java/aurelienribon/utils/notifications/ObservableList.java:
--------------------------------------------------------------------------------
1 | package aurelienribon.utils.notifications;
2 |
3 | import javax.swing.event.EventListenerList;
4 | import java.util.ArrayList;
5 | import java.util.Collection;
6 | import java.util.EventListener;
7 | import java.util.List;
8 |
9 | /**
10 | * @author Aurelien Ribon | http://www.aurelienribon.com
11 | */
12 | public class ObservableList extends ArrayList {
13 | private final Object source;
14 | private final List evtList1 = new ArrayList();
15 | private final List evtList2 = new ArrayList();
16 |
17 | public ObservableList() {
18 | this.source = null;
19 | }
20 |
21 | public ObservableList(Object source) {
22 | this.source = source;
23 | }
24 |
25 | // -------------------------------------------------------------------------
26 | // Public API
27 | // -------------------------------------------------------------------------
28 |
29 | @Override
30 | public boolean add(T e) {
31 | boolean ret = super.add(e);
32 | evtList1.clear();
33 | evtList1.add(e);
34 | fireElementsAdded(evtList1);
35 | return ret;
36 | }
37 |
38 | @Override
39 | public void add(int index, T element) {
40 | super.add(index, element);
41 | evtList1.clear();
42 | evtList1.add(element);
43 | fireElementsAdded(evtList1);
44 | }
45 |
46 | @Override
47 | public boolean addAll(Collection extends T> c) {
48 | boolean ret = super.addAll(c);
49 | evtList1.clear();
50 | evtList1.addAll(c);
51 | fireElementsAdded(evtList1);
52 | return ret;
53 | }
54 |
55 | @Override
56 | public boolean addAll(int index, Collection extends T> c) {
57 | boolean ret = super.addAll(index, c);
58 | evtList1.clear();
59 | evtList1.addAll(c);
60 | fireElementsAdded(evtList1);
61 | return ret;
62 | }
63 |
64 | @Override
65 | public boolean remove(Object o) {
66 | boolean ret = super.remove(o);
67 | if (ret == true) {
68 | evtList1.clear();
69 | evtList1.add((T) o);
70 | fireElementsRemoved(evtList1);
71 | }
72 | return ret;
73 | }
74 |
75 | @Override
76 | public T remove(int index) {
77 | T e = super.remove(index);
78 | if (e != null) {
79 | evtList1.clear();
80 | evtList1.add(e);
81 | fireElementsRemoved(evtList1);
82 | }
83 | return e;
84 | }
85 |
86 | @Override
87 | public boolean removeAll(Collection> c) {
88 | evtList1.clear();
89 | for (Object o : c) if (contains(o)) evtList1.add((T) o);
90 | boolean ret = super.removeAll(c);
91 | if (ret == true) fireElementsRemoved(evtList1);
92 | return ret;
93 | }
94 |
95 | @Override
96 | public boolean retainAll(Collection> c) {
97 | evtList1.clear();
98 | for (T e : this) if (!c.contains(e)) evtList1.add(e);
99 | boolean ret = super.retainAll(c);
100 | if (ret == true) fireElementsRemoved(evtList1);
101 | return ret;
102 | }
103 |
104 | @Override
105 | public void clear() {
106 | evtList1.clear();
107 | evtList1.addAll(this);
108 | super.clear();
109 | fireElementsRemoved(evtList1);
110 | }
111 |
112 | @Override
113 | public T set(int index, T element) {
114 | T e = super.set(index, element);
115 | evtList1.clear();
116 | evtList1.add(e);
117 | fireElementsRemoved(evtList1);
118 | evtList1.clear();
119 | evtList1.add(element);
120 | fireElementsAdded(evtList1);
121 | return e;
122 | }
123 |
124 | public void replaceBy(T element) {
125 | evtList1.clear();
126 | evtList2.clear();
127 | if (!contains(element)) evtList1.add(element);
128 | for (T e : this) if (e != element) evtList2.add(e);
129 | super.clear();
130 | super.add(element);
131 | if (!evtList1.isEmpty() || !evtList2.isEmpty()) fireChanged(evtList1, evtList2);
132 | }
133 |
134 | public void replaceBy(Collection c) {
135 | evtList1.clear();
136 | evtList2.clear();
137 | for (T e : c) if (!contains(e)) evtList1.add(e);
138 | for (T e : this) if (!c.contains(e)) evtList2.add(e);
139 | super.clear();
140 | super.addAll(c);
141 | if (!evtList1.isEmpty() || !evtList2.isEmpty()) fireChanged(evtList1, evtList2);
142 | }
143 |
144 | // -------------------------------------------------------------------------
145 | // Events
146 | // -------------------------------------------------------------------------
147 |
148 | private final EventListenerList listeners = new EventListenerList();
149 |
150 | public static interface ListChangeListener extends EventListener {
151 | public void changed(Object source, List added, List removed);
152 | }
153 |
154 | public void addListChangedListener(ListChangeListener listener) {
155 | listeners.add(ListChangeListener.class, listener);
156 | }
157 |
158 | public void removeListChangedListener(ListChangeListener listener) {
159 | listeners.remove(ListChangeListener.class, listener);
160 | }
161 |
162 | private void fireElementsAdded(List elems) {
163 | if (elems.isEmpty()) return;
164 | for (ListChangeListener listener : listeners.getListeners(ListChangeListener.class))
165 | listener.changed(source != null ? source : this, elems, new ArrayList());
166 | }
167 |
168 | private void fireElementsRemoved(List elems) {
169 | if (elems.isEmpty()) return;
170 | for (ListChangeListener listener : listeners.getListeners(ListChangeListener.class))
171 | listener.changed(source != null ? source : this, new ArrayList(), elems);
172 | }
173 |
174 | private void fireChanged(List added, List removed) {
175 | if (added.isEmpty() && removed.isEmpty()) return;
176 | for (ListChangeListener listener : listeners.getListeners(ListChangeListener.class))
177 | listener.changed(source != null ? source : this, added, removed);
178 | }
179 | }
180 |
--------------------------------------------------------------------------------
/editor/src/main/java/aurelienribon/utils/ui/SwingHelper.java:
--------------------------------------------------------------------------------
1 | package aurelienribon.utils.ui;
2 |
3 | import javax.swing.*;
4 | import java.awt.*;
5 | import java.awt.Desktop.Action;
6 | import java.awt.event.HierarchyEvent;
7 | import java.awt.event.HierarchyListener;
8 | import java.awt.event.WindowListener;
9 | import java.io.IOException;
10 | import java.net.URI;
11 | import java.net.URISyntaxException;
12 |
13 | /**
14 | * @author Aurelien Ribon | http://www.aurelienribon.com
15 | */
16 | public class SwingHelper {
17 | /**
18 | * Adds a listener to the window parent of the given component. Can be
19 | * before the component is really added to its hierachy.
20 | */
21 | public static void addWindowListener(final Component source, final WindowListener listener) {
22 | if (source instanceof Window) {
23 | ((Window) source).addWindowListener(listener);
24 | } else {
25 | source.addHierarchyListener(new HierarchyListener() {
26 | @Override
27 | public void hierarchyChanged(HierarchyEvent e) {
28 | if ((e.getChangeFlags() & HierarchyEvent.SHOWING_CHANGED) == HierarchyEvent.SHOWING_CHANGED) {
29 | SwingUtilities.getWindowAncestor(source).addWindowListener(listener);
30 | }
31 | }
32 | });
33 | }
34 | }
35 |
36 | /**
37 | * Opens the given website in the default browser, or show a message saying
38 | * that no default browser could be accessed.
39 | */
40 | public static void browse(Component parent, String uri) {
41 | boolean error = false;
42 |
43 | if (Desktop.isDesktopSupported() && Desktop.getDesktop().isSupported(Action.BROWSE)) {
44 | try {
45 | Desktop.getDesktop().browse(new URI(uri));
46 | } catch (URISyntaxException ex) {
47 | throw new RuntimeException(ex);
48 | } catch (IOException ex) {
49 | error = true;
50 | }
51 | } else {
52 | error = true;
53 | }
54 |
55 | if (error) {
56 | String msg = "Impossible to open the default browser from the application.\nSorry.";
57 | JOptionPane.showMessageDialog(parent, msg);
58 | }
59 | }
60 |
61 | /**
62 | * Gets the parent JFrame of the component.
63 | */
64 | public static JFrame getFrame(Component cmp) {
65 | return (JFrame) SwingUtilities.getWindowAncestor(cmp);
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/editor/src/main/resources/css/style.css:
--------------------------------------------------------------------------------
1 | /*******************************************************************************
2 | * General
3 | ******************************************************************************/
4 |
5 | .rootPanel {
6 | -ar-fill: texture(url("/gfx/bg.png"));
7 | }
8 |
9 | javax-swing-JScrollPane {
10 | -swing-border: null;
11 | }
12 |
13 | javax-swing-JButton {
14 | -swing-horizontalalignment: left;
15 | -swing-margin: 2 5;
16 | }
17 |
18 | javax-swing-JToolBar javax-swing-JButton {
19 | -swing-horizontalalignment: left;
20 | -swing-margin: 2 1;
21 | }
22 |
23 | javax-swing-JToolBar,
24 | javax-swing-JButton,
25 | javax-swing-JCheckBox,
26 | javax-swing-JSlider{
27 | -swing-opaque: false;
28 | }
29 |
30 | /*******************************************************************************
31 | * Groups
32 | ******************************************************************************/
33 |
34 | .titledPanel {
35 | -swing-background: #FFF;
36 | -swing-border: arui-groupborder(noarg);
37 | -ar-border-stroke: null;
38 | -ar-border-thickness: 0;
39 | -ar-border-header-stroke: null;
40 | -ar-border-header-thickness: 0;
41 | -ar-border-header-fill: lineargradient(0, 0, 0, 1, 0, #4D6082, 1, #3D5277);
42 | -ar-border-margin: 3 6;
43 | -ar-border-foreground: #FFF;
44 | }
45 |
46 | #projectPanel {
47 | -ar-border-title: "Project configuration";
48 | }
49 |
50 | #optionsPanel {
51 | -ar-border-title: "Options";
52 | }
53 |
54 | /*******************************************************************************
55 | * Headers
56 | ******************************************************************************/
57 |
58 | .headerPanel {
59 | -swing-background: #BBC8D8;
60 | }
61 |
62 | .headerPanel javax-swing-JLabel {
63 | -swing-font-size: 10;
64 | -swing-border: emptyborder(2, 5, 2, 5);
65 | -swing-foreground: #3D5277;
66 | -swing-verticalalignment: top;
67 | }
68 |
69 | .headerPanel javax-swing-JButton {
70 | -swing-font-size: 10;
71 | -swing-foreground: #3D5277;
72 | }
73 |
74 | .headerPanel javax-swing-JButton:disabled {
75 | -swing-foreground: #888;
76 | }
77 |
78 | /*******************************************************************************
79 | * Config panels
80 | ******************************************************************************/
81 |
82 | .configPanel aurelienribon-ui-components-PaintedPanel {
83 | -swing-opaque: false;
84 | -ar-fill: #3FFF;
85 | }
86 |
87 | .configPanel aurelienribon-ui-components-PaintedPanel > javax-swing-JLabel,
88 | .configPanel aurelienribon-ui-components-PaintedPanel > javax-swing-JCheckBox {
89 | -swing-foreground: #FFF;
90 | }
91 |
92 | .configPanel .bigLabel {
93 | -swing-foreground: #FFF;
94 | -swing-horizontalalignment: center;
95 | }
96 |
97 | /*******************************************************************************
98 | * TabPanel
99 | ******************************************************************************/
100 |
101 | aurelienribon-ui-components-TabPanel {
102 | -swing-background: #BBC8D8;
103 | }
104 |
105 | aurelienribon-ui-components-TabPanel .-ar-tabcontent {
106 | -swing-background: #FFF;
107 | }
108 |
109 | aurelienribon-ui-components-TabPanel .-ar-header {
110 | -ar-stroke: null;
111 | }
112 |
113 | aurelienribon-ui-components-TabPanel .-ar-tab {
114 | -swing-foreground: #FFF;
115 | -ar-stroke: null;
116 | -ar-fill: null;
117 | }
118 |
119 | aurelienribon-ui-components-TabPanel .-ar-tab:hover {
120 | -ar-stroke: #9BA7B7;
121 | -ar-fill: lineargradient(0, 0, 0, 1, 0, #6F7776, 1, #4F5F74);
122 | }
123 |
124 | aurelienribon-ui-components-TabPanel .-ar-tab:selected {
125 | -ar-stroke: null;
126 | -ar-fill: lineargradient(0, 0, 0, 1, 0, #4D6082, 1, #3D5277);
127 | }
128 |
129 | /*******************************************************************************
130 | * Misc
131 | ******************************************************************************/
132 |
133 | .leftPanel {
134 | -swing-border: matteborder(0, 0, 0, 1, #000);
135 | }
136 |
137 | .brightlink {
138 | -swing-foreground: #FFF;
139 | }
140 |
141 | .brightlink:hover {
142 | -swing-foreground: #AAF;
143 | }
144 |
145 | .darklink {
146 | -swing-foreground: #228;
147 | }
148 |
149 | .darklink:hover {
150 | -swing-foreground: #003;
151 | }
152 |
153 | .bold {
154 | -swing-font-style: bold;
155 | }
156 |
157 | .brightcomment {
158 | -swing-foreground: #AAA;
159 | }
160 |
161 | .versionLabel {
162 | -swing-foreground: #000;
163 | -swing-border: emptyborder(2, 10, 2, 10);
164 | -swing-font-size: 10;
165 | }
166 |
167 | .versionPanel {
168 | -swing-background: #FFF;
169 | -swing-border: matteborder(1, 0, 0, 0, #2A3B57);
170 | }
--------------------------------------------------------------------------------
/editor/src/main/resources/data/ball.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MovingBlocks/box2d-editor/05c7ea15b80fff859c6abc91c1761979cbfd31a3/editor/src/main/resources/data/ball.png
--------------------------------------------------------------------------------
/editor/src/main/resources/data/transparent-dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MovingBlocks/box2d-editor/05c7ea15b80fff859c6abc91c1761979cbfd31a3/editor/src/main/resources/data/transparent-dark.png
--------------------------------------------------------------------------------
/editor/src/main/resources/data/transparent-light.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MovingBlocks/box2d-editor/05c7ea15b80fff859c6abc91c1761979cbfd31a3/editor/src/main/resources/data/transparent-light.png
--------------------------------------------------------------------------------
/editor/src/main/resources/data/unknown.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MovingBlocks/box2d-editor/05c7ea15b80fff859c6abc91c1761979cbfd31a3/editor/src/main/resources/data/unknown.png
--------------------------------------------------------------------------------
/editor/src/main/resources/data/v00.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MovingBlocks/box2d-editor/05c7ea15b80fff859c6abc91c1761979cbfd31a3/editor/src/main/resources/data/v00.png
--------------------------------------------------------------------------------
/editor/src/main/resources/data/v01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MovingBlocks/box2d-editor/05c7ea15b80fff859c6abc91c1761979cbfd31a3/editor/src/main/resources/data/v01.png
--------------------------------------------------------------------------------
/editor/src/main/resources/data/v10.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MovingBlocks/box2d-editor/05c7ea15b80fff859c6abc91c1761979cbfd31a3/editor/src/main/resources/data/v10.png
--------------------------------------------------------------------------------
/editor/src/main/resources/data/white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MovingBlocks/box2d-editor/05c7ea15b80fff859c6abc91c1761979cbfd31a3/editor/src/main/resources/data/white.png
--------------------------------------------------------------------------------
/editor/src/main/resources/gfx/autoTrace.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MovingBlocks/box2d-editor/05c7ea15b80fff859c6abc91c1761979cbfd31a3/editor/src/main/resources/gfx/autoTrace.png
--------------------------------------------------------------------------------
/editor/src/main/resources/gfx/bg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MovingBlocks/box2d-editor/05c7ea15b80fff859c6abc91c1761979cbfd31a3/editor/src/main/resources/gfx/bg.png
--------------------------------------------------------------------------------
/editor/src/main/resources/gfx/comingSoon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MovingBlocks/box2d-editor/05c7ea15b80fff859c6abc91c1761979cbfd31a3/editor/src/main/resources/gfx/comingSoon.png
--------------------------------------------------------------------------------
/editor/src/main/resources/gfx/ic_add.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MovingBlocks/box2d-editor/05c7ea15b80fff859c6abc91c1761979cbfd31a3/editor/src/main/resources/gfx/ic_add.png
--------------------------------------------------------------------------------
/editor/src/main/resources/gfx/ic_createShape.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MovingBlocks/box2d-editor/05c7ea15b80fff859c6abc91c1761979cbfd31a3/editor/src/main/resources/gfx/ic_createShape.png
--------------------------------------------------------------------------------
/editor/src/main/resources/gfx/ic_delete.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MovingBlocks/box2d-editor/05c7ea15b80fff859c6abc91c1761979cbfd31a3/editor/src/main/resources/gfx/ic_delete.png
--------------------------------------------------------------------------------
/editor/src/main/resources/gfx/ic_down.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MovingBlocks/box2d-editor/05c7ea15b80fff859c6abc91c1761979cbfd31a3/editor/src/main/resources/gfx/ic_down.png
--------------------------------------------------------------------------------
/editor/src/main/resources/gfx/ic_edit.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MovingBlocks/box2d-editor/05c7ea15b80fff859c6abc91c1761979cbfd31a3/editor/src/main/resources/gfx/ic_edit.png
--------------------------------------------------------------------------------
/editor/src/main/resources/gfx/ic_editShape.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MovingBlocks/box2d-editor/05c7ea15b80fff859c6abc91c1761979cbfd31a3/editor/src/main/resources/gfx/ic_editShape.png
--------------------------------------------------------------------------------
/editor/src/main/resources/gfx/ic_error.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MovingBlocks/box2d-editor/05c7ea15b80fff859c6abc91c1761979cbfd31a3/editor/src/main/resources/gfx/ic_error.png
--------------------------------------------------------------------------------
/editor/src/main/resources/gfx/ic_export.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MovingBlocks/box2d-editor/05c7ea15b80fff859c6abc91c1761979cbfd31a3/editor/src/main/resources/gfx/ic_export.png
--------------------------------------------------------------------------------
/editor/src/main/resources/gfx/ic_file.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MovingBlocks/box2d-editor/05c7ea15b80fff859c6abc91c1761979cbfd31a3/editor/src/main/resources/gfx/ic_file.png
--------------------------------------------------------------------------------
/editor/src/main/resources/gfx/ic_gear.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MovingBlocks/box2d-editor/05c7ea15b80fff859c6abc91c1761979cbfd31a3/editor/src/main/resources/gfx/ic_gear.png
--------------------------------------------------------------------------------
/editor/src/main/resources/gfx/ic_help.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MovingBlocks/box2d-editor/05c7ea15b80fff859c6abc91c1761979cbfd31a3/editor/src/main/resources/gfx/ic_help.png
--------------------------------------------------------------------------------
/editor/src/main/resources/gfx/ic_import.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MovingBlocks/box2d-editor/05c7ea15b80fff859c6abc91c1761979cbfd31a3/editor/src/main/resources/gfx/ic_import.png
--------------------------------------------------------------------------------
/editor/src/main/resources/gfx/ic_loading.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MovingBlocks/box2d-editor/05c7ea15b80fff859c6abc91c1761979cbfd31a3/editor/src/main/resources/gfx/ic_loading.gif
--------------------------------------------------------------------------------
/editor/src/main/resources/gfx/ic_lock.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MovingBlocks/box2d-editor/05c7ea15b80fff859c6abc91c1761979cbfd31a3/editor/src/main/resources/gfx/ic_lock.png
--------------------------------------------------------------------------------
/editor/src/main/resources/gfx/ic_manual.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MovingBlocks/box2d-editor/05c7ea15b80fff859c6abc91c1761979cbfd31a3/editor/src/main/resources/gfx/ic_manual.png
--------------------------------------------------------------------------------
/editor/src/main/resources/gfx/ic_new.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MovingBlocks/box2d-editor/05c7ea15b80fff859c6abc91c1761979cbfd31a3/editor/src/main/resources/gfx/ic_new.png
--------------------------------------------------------------------------------
/editor/src/main/resources/gfx/ic_ok.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MovingBlocks/box2d-editor/05c7ea15b80fff859c6abc91c1761979cbfd31a3/editor/src/main/resources/gfx/ic_ok.png
--------------------------------------------------------------------------------
/editor/src/main/resources/gfx/ic_open.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MovingBlocks/box2d-editor/05c7ea15b80fff859c6abc91c1761979cbfd31a3/editor/src/main/resources/gfx/ic_open.png
--------------------------------------------------------------------------------
/editor/src/main/resources/gfx/ic_remove.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MovingBlocks/box2d-editor/05c7ea15b80fff859c6abc91c1761979cbfd31a3/editor/src/main/resources/gfx/ic_remove.png
--------------------------------------------------------------------------------
/editor/src/main/resources/gfx/ic_save.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MovingBlocks/box2d-editor/05c7ea15b80fff859c6abc91c1761979cbfd31a3/editor/src/main/resources/gfx/ic_save.png
--------------------------------------------------------------------------------
/editor/src/main/resources/gfx/ic_shape.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MovingBlocks/box2d-editor/05c7ea15b80fff859c6abc91c1761979cbfd31a3/editor/src/main/resources/gfx/ic_shape.png
--------------------------------------------------------------------------------
/editor/src/main/resources/gfx/ic_test.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MovingBlocks/box2d-editor/05c7ea15b80fff859c6abc91c1761979cbfd31a3/editor/src/main/resources/gfx/ic_test.png
--------------------------------------------------------------------------------
/editor/src/main/resources/gfx/ic_texture.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MovingBlocks/box2d-editor/05c7ea15b80fff859c6abc91c1761979cbfd31a3/editor/src/main/resources/gfx/ic_texture.png
--------------------------------------------------------------------------------
/editor/src/main/resources/gfx/ic_unlock.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MovingBlocks/box2d-editor/05c7ea15b80fff859c6abc91c1761979cbfd31a3/editor/src/main/resources/gfx/ic_unlock.png
--------------------------------------------------------------------------------
/editor/src/main/resources/gfx/ic_up.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MovingBlocks/box2d-editor/05c7ea15b80fff859c6abc91c1761979cbfd31a3/editor/src/main/resources/gfx/ic_up.png
--------------------------------------------------------------------------------
/editor/src/main/resources/gfx/ic_warning.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MovingBlocks/box2d-editor/05c7ea15b80fff859c6abc91c1761979cbfd31a3/editor/src/main/resources/gfx/ic_warning.png
--------------------------------------------------------------------------------
/editor/src/main/resources/gfx/ic_wrench.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MovingBlocks/box2d-editor/05c7ea15b80fff859c6abc91c1761979cbfd31a3/editor/src/main/resources/gfx/ic_wrench.png
--------------------------------------------------------------------------------
/editor/src/main/resources/gfx/newBody.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MovingBlocks/box2d-editor/05c7ea15b80fff859c6abc91c1761979cbfd31a3/editor/src/main/resources/gfx/newBody.png
--------------------------------------------------------------------------------
/editor/src/main/resources/gfx/title.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MovingBlocks/box2d-editor/05c7ea15b80fff859c6abc91c1761979cbfd31a3/editor/src/main/resources/gfx/title.png
--------------------------------------------------------------------------------
/editor/src/main/resources/gfx/unknown.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MovingBlocks/box2d-editor/05c7ea15b80fff859c6abc91c1761979cbfd31a3/editor/src/main/resources/gfx/unknown.png
--------------------------------------------------------------------------------
/editor/test-me!/gfx/test01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MovingBlocks/box2d-editor/05c7ea15b80fff859c6abc91c1761979cbfd31a3/editor/test-me!/gfx/test01.png
--------------------------------------------------------------------------------
/editor/test-me!/gfx/test02 (non POT).png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MovingBlocks/box2d-editor/05c7ea15b80fff859c6abc91c1761979cbfd31a3/editor/test-me!/gfx/test02 (non POT).png
--------------------------------------------------------------------------------
/editor/test-me!/gfx/test03 (multi shapes).png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MovingBlocks/box2d-editor/05c7ea15b80fff859c6abc91c1761979cbfd31a3/editor/test-me!/gfx/test03 (multi shapes).png
--------------------------------------------------------------------------------
/editor/test-me!/gfx/test04 (non square).png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MovingBlocks/box2d-editor/05c7ea15b80fff859c6abc91c1761979cbfd31a3/editor/test-me!/gfx/test04 (non square).png
--------------------------------------------------------------------------------
/editor/test-me!/gfx/test05 (non square).png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MovingBlocks/box2d-editor/05c7ea15b80fff859c6abc91c1761979cbfd31a3/editor/test-me!/gfx/test05 (non square).png
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | org.gradle.daemon=true
2 | org.gradle.jvmargs=-Xms128m -Xmx512m
3 | org.gradle.configureondemand=true
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MovingBlocks/box2d-editor/05c7ea15b80fff859c6abc91c1761979cbfd31a3/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip
4 | networkTimeout=10000
5 | validateDistributionUrl=true
6 | zipStoreBase=GRADLE_USER_HOME
7 | zipStorePath=wrapper/dists
8 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 |
17 | @if "%DEBUG%"=="" @echo off
18 | @rem ##########################################################################
19 | @rem
20 | @rem Gradle startup script for Windows
21 | @rem
22 | @rem ##########################################################################
23 |
24 | @rem Set local scope for the variables with windows NT shell
25 | if "%OS%"=="Windows_NT" setlocal
26 |
27 | set DIRNAME=%~dp0
28 | if "%DIRNAME%"=="" set DIRNAME=.
29 | @rem This is normally unused
30 | set APP_BASE_NAME=%~n0
31 | set APP_HOME=%DIRNAME%
32 |
33 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
34 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
35 |
36 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
37 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
38 |
39 | @rem Find java.exe
40 | if defined JAVA_HOME goto findJavaFromJavaHome
41 |
42 | set JAVA_EXE=java.exe
43 | %JAVA_EXE% -version >NUL 2>&1
44 | if %ERRORLEVEL% equ 0 goto execute
45 |
46 | echo.
47 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
48 | echo.
49 | echo Please set the JAVA_HOME variable in your environment to match the
50 | echo location of your Java installation.
51 |
52 | goto fail
53 |
54 | :findJavaFromJavaHome
55 | set JAVA_HOME=%JAVA_HOME:"=%
56 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
57 |
58 | if exist "%JAVA_EXE%" goto execute
59 |
60 | echo.
61 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
62 | echo.
63 | echo Please set the JAVA_HOME variable in your environment to match the
64 | echo location of your Java installation.
65 |
66 | goto fail
67 |
68 | :execute
69 | @rem Setup the command line
70 |
71 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
72 |
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if %ERRORLEVEL% equ 0 goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | set EXIT_CODE=%ERRORLEVEL%
85 | if %EXIT_CODE% equ 0 set EXIT_CODE=1
86 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
87 | exit /b %EXIT_CODE%
88 |
89 | :mainEnd
90 | if "%OS%"=="Windows_NT" endlocal
91 |
92 | :omega
93 |
--------------------------------------------------------------------------------
/loader-libgdx-demo/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'application'
3 | }
4 |
5 | sourceCompatibility = 1.8
6 | [compileJava, compileTestJava]*.options*.encoding = 'UTF-8'
7 |
8 | dependencies {
9 | implementation "com.badlogicgames.gdx:gdx:$gdxVersion"
10 | implementation "com.badlogicgames.gdx:gdx-backend-lwjgl:$gdxVersion"
11 | implementation "com.badlogicgames.gdx:gdx-box2d:$gdxVersion"
12 | implementation "com.badlogicgames.gdx:gdx-box2d-platform:$gdxVersion:natives-desktop"
13 | implementation "com.badlogicgames.gdx:gdx-platform:$gdxVersion"
14 | implementation "com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-desktop"
15 |
16 | implementation files('./libs/tween-engine-api.jar')
17 |
18 | implementation project(':loader-libgdx')
19 |
20 | }
21 |
22 | sourceSets {
23 | main {
24 | java.srcDirs = ["src/main/java"]
25 | resources.srcDirs = ["src/main/resources"]
26 | }
27 | }
28 |
29 | application {
30 | mainClass = 'aurelienribon.bodyeditor.Main'
31 | }
32 |
33 | tasks.withType(Exec) {
34 | group = 'loader-demo'
35 | }
36 |
37 | jar {
38 | archiveFileName = "loader-libgdx-demo.jar"
39 | }
40 |
41 | eclipse.project {
42 | name = appName + "-loader-libgdx-demo"
43 | }
44 |
45 | // Mark the resources dir in IntelliJ as an actual resource dir, not another java source dir
46 | // Derived from https://discuss.gradle.org/t/13391/2 but needs a TODO for Gradle import style vs XML hacking
47 | idea {
48 | module.iml.withXml {
49 | def node = it.asNode()
50 | def content = node.component.find { it.'@name' == 'NewModuleRootManager' }.content[0]
51 | content.sourceFolder.each { sourceFolder ->
52 | if (sourceFolder.@url?.endsWith('/resources')) {
53 | sourceFolder.attributes().with {
54 | boolean isTestSource = (remove('isTestSource') == 'true')
55 | put('type', isTestSource ? 'java-test-resource' : 'java-resource')
56 | }
57 | }
58 | }
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/loader-libgdx-demo/libs/gdx-backend-lwjgl-natives.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MovingBlocks/box2d-editor/05c7ea15b80fff859c6abc91c1761979cbfd31a3/loader-libgdx-demo/libs/gdx-backend-lwjgl-natives.jar
--------------------------------------------------------------------------------
/loader-libgdx-demo/libs/gdx-backend-lwjgl-sources.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MovingBlocks/box2d-editor/05c7ea15b80fff859c6abc91c1761979cbfd31a3/loader-libgdx-demo/libs/gdx-backend-lwjgl-sources.jar
--------------------------------------------------------------------------------
/loader-libgdx-demo/libs/gdx-backend-lwjgl.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MovingBlocks/box2d-editor/05c7ea15b80fff859c6abc91c1761979cbfd31a3/loader-libgdx-demo/libs/gdx-backend-lwjgl.jar
--------------------------------------------------------------------------------
/loader-libgdx-demo/libs/gdx-natives.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MovingBlocks/box2d-editor/05c7ea15b80fff859c6abc91c1761979cbfd31a3/loader-libgdx-demo/libs/gdx-natives.jar
--------------------------------------------------------------------------------
/loader-libgdx-demo/libs/gdx-sources.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MovingBlocks/box2d-editor/05c7ea15b80fff859c6abc91c1761979cbfd31a3/loader-libgdx-demo/libs/gdx-sources.jar
--------------------------------------------------------------------------------
/loader-libgdx-demo/libs/gdx.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MovingBlocks/box2d-editor/05c7ea15b80fff859c6abc91c1761979cbfd31a3/loader-libgdx-demo/libs/gdx.jar
--------------------------------------------------------------------------------
/loader-libgdx-demo/libs/tween-engine-api-sources.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MovingBlocks/box2d-editor/05c7ea15b80fff859c6abc91c1761979cbfd31a3/loader-libgdx-demo/libs/tween-engine-api-sources.jar
--------------------------------------------------------------------------------
/loader-libgdx-demo/libs/tween-engine-api.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MovingBlocks/box2d-editor/05c7ea15b80fff859c6abc91c1761979cbfd31a3/loader-libgdx-demo/libs/tween-engine-api.jar
--------------------------------------------------------------------------------
/loader-libgdx-demo/src/main/java/aurelienribon/bodyeditor/Main.java:
--------------------------------------------------------------------------------
1 | package aurelienribon.bodyeditor;
2 |
3 | import com.badlogic.gdx.backends.lwjgl.LwjglApplication;
4 |
5 | public class Main {
6 | public static void main(final String[] args) {
7 | // When this app was written against an older LibGDX the boolean previously here was setting GL10 vs GL20. Now always 20.
8 | new LwjglApplication(new App(), "Physics Body Editor - Loader Demo", 500, 600);
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/loader-libgdx-demo/src/main/resources/data/gfx/ball.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MovingBlocks/box2d-editor/05c7ea15b80fff859c6abc91c1761979cbfd31a3/loader-libgdx-demo/src/main/resources/data/gfx/ball.png
--------------------------------------------------------------------------------
/loader-libgdx-demo/src/main/resources/data/gfx/bottle.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MovingBlocks/box2d-editor/05c7ea15b80fff859c6abc91c1761979cbfd31a3/loader-libgdx-demo/src/main/resources/data/gfx/bottle.png
--------------------------------------------------------------------------------
/loader-libgdx-demo/src/main/resources/data/gfx/white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MovingBlocks/box2d-editor/05c7ea15b80fff859c6abc91c1761979cbfd31a3/loader-libgdx-demo/src/main/resources/data/gfx/white.png
--------------------------------------------------------------------------------
/loader-libgdx/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'java-library'
3 | }
4 |
5 | sourceCompatibility = 1.8
6 | [compileJava, compileTestJava]*.options*.encoding = 'UTF-8'
7 |
8 | dependencies {
9 | implementation "com.badlogicgames.gdx:gdx:$gdxVersion"
10 | implementation "com.badlogicgames.gdx:gdx-box2d:$gdxVersion"
11 | implementation "com.badlogicgames.gdx:gdx-backend-lwjgl:$gdxVersion"
12 | implementation "com.badlogicgames.gdx:gdx-box2d-platform:$gdxVersion:natives-desktop"
13 | implementation "com.badlogicgames.gdx:gdx-platform:$gdxVersion"
14 | implementation "com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-desktop"
15 | }
16 |
17 | sourceSets {
18 | main {
19 | java.srcDirs = ["src"]
20 | }
21 | }
22 |
23 | jar {
24 | archiveFileName = "loader-libgdx.jar"
25 | }
26 |
27 | eclipse.project {
28 | name = appName + "-loader-libgdx"
29 | }
30 |
--------------------------------------------------------------------------------
/loader-libgdx/src/aurelienribon/bodyeditorloader.gwt.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/readmeImgs/aurelienribon - box2d-editor is apache 2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MovingBlocks/box2d-editor/05c7ea15b80fff859c6abc91c1761979cbfd31a3/readmeImgs/aurelienribon - box2d-editor is apache 2.png
--------------------------------------------------------------------------------
/readmeImgs/pbe-02.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MovingBlocks/box2d-editor/05c7ea15b80fff859c6abc91c1761979cbfd31a3/readmeImgs/pbe-02.jpg
--------------------------------------------------------------------------------
/readmeImgs/pbe-04.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MovingBlocks/box2d-editor/05c7ea15b80fff859c6abc91c1761979cbfd31a3/readmeImgs/pbe-04.jpg
--------------------------------------------------------------------------------
/readmeImgs/pbe-autotrace.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MovingBlocks/box2d-editor/05c7ea15b80fff859c6abc91c1761979cbfd31a3/readmeImgs/pbe-autotrace.jpg
--------------------------------------------------------------------------------
/readmeImgs/pbe-circle-shapes.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MovingBlocks/box2d-editor/05c7ea15b80fff859c6abc91c1761979cbfd31a3/readmeImgs/pbe-circle-shapes.jpg
--------------------------------------------------------------------------------
/readmeImgs/pbe-loader-demo.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MovingBlocks/box2d-editor/05c7ea15b80fff859c6abc91c1761979cbfd31a3/readmeImgs/pbe-loader-demo.jpg
--------------------------------------------------------------------------------
/readmeImgs/pbe-reference-point.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MovingBlocks/box2d-editor/05c7ea15b80fff859c6abc91c1761979cbfd31a3/readmeImgs/pbe-reference-point.jpg
--------------------------------------------------------------------------------
/readmeImgs/pbe-workflow.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MovingBlocks/box2d-editor/05c7ea15b80fff859c6abc91c1761979cbfd31a3/readmeImgs/pbe-workflow.jpg
--------------------------------------------------------------------------------
/readmeImgs/slide-physics-body-editor.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MovingBlocks/box2d-editor/05c7ea15b80fff859c6abc91c1761979cbfd31a3/readmeImgs/slide-physics-body-editor.jpg
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include 'editor', 'loader-libgdx', 'loader-libgdx-demo'
2 |
--------------------------------------------------------------------------------