From 398f3268f53a1fe3a0f253b13c8b85c8aeb0bc64 Mon Sep 17 00:00:00 2001 From: James Ball Date: Sat, 9 Sep 2023 11:22:14 +0100 Subject: [PATCH] Fix SVG parsing bugs, object parsing bugs, rotation precision errors --- Source/MathUtil.h | 13 +++++++++++++ Source/audio/EffectApplication.cpp | 10 ++++------ Source/audio/PerspectiveEffect.cpp | 17 ++++------------ Source/audio/PerspectiveEffect.h | 10 +++++++--- Source/chinese_postman/Graph.cpp | 6 ++++-- Source/obj/WorldObject.cpp | 7 ++++--- Source/shape/Vector2.cpp | 2 +- Source/svg/SvgParser.cpp | 31 +++++++++++++++++++----------- osci-render.jucer | 1 + 9 files changed, 58 insertions(+), 39 deletions(-) create mode 100644 Source/MathUtil.h diff --git a/Source/MathUtil.h b/Source/MathUtil.h new file mode 100644 index 0000000..2187fe4 --- /dev/null +++ b/Source/MathUtil.h @@ -0,0 +1,13 @@ +#pragma once + +#include + +class MathUtil { +public: + + // from https://stackoverflow.com/questions/11980292/how-to-wrap-around-a-range + static inline double wrapAngle(double angle) { + double twoPi = 2.0 * std::numbers::pi; + return angle - twoPi * floor(angle / twoPi); + } +}; diff --git a/Source/audio/EffectApplication.cpp b/Source/audio/EffectApplication.cpp index 08de7b4..22afa1a 100644 --- a/Source/audio/EffectApplication.cpp +++ b/Source/audio/EffectApplication.cpp @@ -1,16 +1,14 @@ #include "EffectApplication.h" #include +#include "../MathUtil.h" void EffectApplication::resetPhase() { phase = 0.0; } double EffectApplication::nextPhase(double frequency, double sampleRate) { - phase += frequency / sampleRate; + phase += 2 * std::numbers::pi * frequency / sampleRate; + phase = MathUtil::wrapAngle(phase); - if (phase > 1) { - phase -= 1; - } - - return phase * 2 * std::numbers::pi; + return phase; } diff --git a/Source/audio/PerspectiveEffect.cpp b/Source/audio/PerspectiveEffect.cpp index 518d7a3..1a56a12 100644 --- a/Source/audio/PerspectiveEffect.cpp +++ b/Source/audio/PerspectiveEffect.cpp @@ -1,5 +1,6 @@ #include "PerspectiveEffect.h" #include +#include "../MathUtil.h" PerspectiveEffect::PerspectiveEffect(int versionHint) : versionHint(versionHint) { fixedRotateX = new BooleanParameter("Perspective Fixed Rotate X", "perspectiveFixedRotateX", versionHint, false); @@ -31,19 +32,9 @@ Vector2 PerspectiveEffect::apply(int index, Vector2 input, const std::vector std::numbers::pi * 8) { - currentRotateX -= std::numbers::pi * 8; - } - if (currentRotateY > std::numbers::pi * 8) { - currentRotateY -= std::numbers::pi * 8; - } - if (currentRotateZ > std::numbers::pi * 8) { - currentRotateZ -= std::numbers::pi * 8; - } + currentRotateX = MathUtil::wrapAngle(currentRotateX + baseRotateX * rotateSpeed); + currentRotateY = MathUtil::wrapAngle(currentRotateY + baseRotateY * rotateSpeed); + currentRotateZ = MathUtil::wrapAngle(currentRotateZ + baseRotateZ * rotateSpeed); auto x = input.x; auto y = input.y; diff --git a/Source/audio/PerspectiveEffect.h b/Source/audio/PerspectiveEffect.h index a86d92a..08f819b 100644 --- a/Source/audio/PerspectiveEffect.h +++ b/Source/audio/PerspectiveEffect.h @@ -28,7 +28,11 @@ private: int versionHint; - float linearSpeedToActualSpeed(float rotateSpeed) { - return (std::exp(3 * juce::jmin(10.0f, std::abs(rotateSpeed))) - 1) / 50000.0; - } + double linearSpeedToActualSpeed(double rotateSpeed) { + double actualSpeed = (std::exp(3 * std::min(10.0, std::abs(rotateSpeed))) - 1) / 50000; + if (rotateSpeed < 0) { + actualSpeed *= -1; + } + return actualSpeed; + } }; diff --git a/Source/chinese_postman/Graph.cpp b/Source/chinese_postman/Graph.cpp index 50e9044..d2b4fe4 100644 --- a/Source/chinese_postman/Graph.cpp +++ b/Source/chinese_postman/Graph.cpp @@ -12,8 +12,10 @@ Graph::Graph(int n, list< pair > & edges): { int u = (*it).first; int v = (*it).second; - - AddEdge(u, v); + + if (v < n && u < n) { + AddEdge(u, v); + } } } diff --git a/Source/obj/WorldObject.cpp b/Source/obj/WorldObject.cpp index b94df1b..6ca2b7c 100644 --- a/Source/obj/WorldObject.cpp +++ b/Source/obj/WorldObject.cpp @@ -1,6 +1,7 @@ #include "WorldObject.h" #include "../chinese_postman/ChinesePostman.h" #include "tiny_obj_loader.h" +#include "../MathUtil.h" struct pair_hash { inline std::size_t operator()(const std::pair& v) const { @@ -238,9 +239,9 @@ void WorldObject::setRotationSpeed(double rotateSpeed) { // called whenever a new frame is drawn, so that the object can update its // rotation void WorldObject::nextFrame() { - currentRotateX = currentRotateX + baseRotateX * rotateSpeed; - currentRotateY = currentRotateY + baseRotateY * rotateSpeed; - currentRotateZ = currentRotateZ + baseRotateZ * rotateSpeed; + currentRotateX = MathUtil::wrapAngle(currentRotateX + baseRotateX * rotateSpeed); + currentRotateY = MathUtil::wrapAngle(currentRotateY + baseRotateY * rotateSpeed); + currentRotateZ = MathUtil::wrapAngle(currentRotateZ + baseRotateZ * rotateSpeed); rotateX = baseRotateX + currentRotateX; rotateY = baseRotateY + currentRotateY; rotateZ = baseRotateZ + currentRotateZ; diff --git a/Source/shape/Vector2.cpp b/Source/shape/Vector2.cpp index 0aa0273..62b0f8a 100644 --- a/Source/shape/Vector2.cpp +++ b/Source/shape/Vector2.cpp @@ -45,7 +45,7 @@ double Vector2::magnitude() { } std::unique_ptr Vector2::clone() { - return std::unique_ptr(); + return std::make_unique(x, y); } std::string Vector2::type() { diff --git a/Source/svg/SvgParser.cpp b/Source/svg/SvgParser.cpp index 8c5ac4a..a49ef18 100644 --- a/Source/svg/SvgParser.cpp +++ b/Source/svg/SvgParser.cpp @@ -5,17 +5,26 @@ SvgParser::SvgParser(juce::String svgFile) { auto doc = juce::XmlDocument::parse(svgFile); - std::unique_ptr svg = juce::Drawable::createFromSVG(*doc); - juce::DrawableComposite* composite = dynamic_cast(svg.get()); - auto contentArea = composite->getContentArea(); - auto path = svg->getOutlineAsPath(); - // apply transform to path to get the content area in the bounds -1 to 1 - path.applyTransform(juce::AffineTransform::translation(-contentArea.getX(), -contentArea.getY())); - path.applyTransform(juce::AffineTransform::scale(2 / contentArea.getWidth(), 2 / contentArea.getHeight())); - path.applyTransform(juce::AffineTransform::translation(-1, -1)); + if (doc != nullptr) { + std::unique_ptr svg = juce::Drawable::createFromSVG(*doc); + juce::DrawableComposite* composite = dynamic_cast(svg.get()); + if (composite != nullptr) { + auto contentArea = composite->getContentArea(); + auto path = svg->getOutlineAsPath(); + // apply transform to path to get the content area in the bounds -1 to 1 + path.applyTransform(juce::AffineTransform::translation(-contentArea.getX(), -contentArea.getY())); + path.applyTransform(juce::AffineTransform::scale(2 / contentArea.getWidth(), 2 / contentArea.getHeight())); + path.applyTransform(juce::AffineTransform::translation(-1, -1)); - pathToShapes(path, shapes); - Shape::removeOutOfBounds(shapes); + pathToShapes(path, shapes); + Shape::removeOutOfBounds(shapes); + return; + } + } + + // draw an X to indicate an error. + shapes.push_back(std::make_unique(-0.5, -0.5, 0.5, 0.5)); + shapes.push_back(std::make_unique(-0.5, 0.5, 0.5, -0.5)); } SvgParser::~SvgParser() {} @@ -69,4 +78,4 @@ std::vector> SvgParser::draw() { tempShapes.push_back(shape->clone()); } return tempShapes; -} \ No newline at end of file +} diff --git a/osci-render.jucer b/osci-render.jucer index 8184f41..a905709 100644 --- a/osci-render.jucer +++ b/osci-render.jucer @@ -380,6 +380,7 @@ +