SIGPX2 - Siv3D/Reference-JP GitHub Wiki

2016 年 8 月 7 日 SIGPX2

Siv3D デモ

1. 図形とマウス

# include <Siv3D.hpp>

void Main()
{
    const Font font(30);

    while (System::Update())
    {
        Circle(Mouse::Pos(), 100).draw();

        font(Mouse::Pos()).draw(50, 200, Palette::Orange);
    }
}

2. マイク FFT

# include <Siv3D.hpp>

void Main()
{
    Window::Resize(1024, 320);

    const Font font(30);

    Recorder recorder(0, 5s, RecordingFormat::S44100, true);

    if (!recorder.start())
    {
        return;
    }

    while (System::Update())
    {
        const auto fft = FFT::Analyze(recorder);

        for (auto i : step(1024))
        {
            RectF(i, Window::Height(), 1, -Pow(fft.buffer[i], 0.5) * 500).draw(HSV(i));
        }

        const int32 mouseX = Mouse::Pos().x;

        Rect(mouseX, 0, 1, Window::Height()).draw();

        font(L"{:.1f}Hz"_fmt, fft.resolution() * mouseX).draw(Mouse::Pos().moveBy(0, -50));
    }
}

3. AR

# include <Siv3D.hpp>

void Main()
{
	Webcam webcam(0);
	webcam.start();
	webcam.startAR();

	DynamicTexture texture;
	Array<ARMarker> markers;

	const Font font(40, Typeface::Bold);

	while (System::Update())
	{
		if (webcam.hasNewFrame())
		{
			webcam.getFrame(texture);
		}

		if (webcam.hasNewMarkers())
		{
			webcam.getMarkers(markers);
		}

		texture.draw();

		for (const auto& marker : markers)
		{
			marker.quad.drawFrame(4, Palette::Red);

			font(marker.id).drawCenter(marker.screenPos, Palette::Red);
		}
	}
}

4. 対称定規

# include <Siv3D.hpp>

void Main()
{
    Window::Resize(720, 720);

    const int32 N = 12;

    Image image(Window::Size(), Color(20, 40, 60));

    DynamicTexture texture(image);

    Mouse::SetTransform(Mat3x2::Translate(Window::Center()));

    while (System::Update())
    {
        if (Input::MouseL.pressed)
        {
            for (auto i : step(N))
            {
                Circular cs[2] = { Input::MouseL.clicked ? Mouse::Pos() : Mouse::PreviousPos(), Mouse::Pos() };

                for (auto& c : cs)
                {
                    c.theta = i % 2 ? -c.theta - TwoPi / N * (i - 1) : c.theta + TwoPi / N * i;
                }

                Line(cs[0], cs[1]).moveBy(Window::Center()).overwrite(image, 2, HSV(System::FrameCount(), 0.5, 1.0));
            }

            texture.tryFill(image);
        }
        else if (Input::MouseR.clicked)
        {
            image.fill(Color(20, 40, 60));

            texture.fill(image);
        }

        texture.draw();
    }
}

5. ツイート投稿

# include <Siv3D.hpp>

void Main()
{
    Graphics::SetBackground(Color(160, 200, 100));

    // Consumer Key (API Key)
    const String API_key = L"XXXXXXXXXXXXXXXXXXXXXXXXXXXX";

    // Consumer Secret (API Secret)
    const String API_secret = L"XXXXXXXXXXXXXXXXXXXXXXXXXXXX";

    TwitterClient twitter(API_key, API_secret);
    twitter.openTokenPage();

    GUI guiAuth(GUIStyle::Default);
    guiAuth.setTitle(L"PIN の入力");
    guiAuth.addln(L"PIN", GUITextField::Create(7));
    guiAuth.add(L"auth", GUIButton::Create(L"認証"));
    guiAuth.setPos(40, 40);

    GUI guiTweet(GUIStyle::Default);
    guiTweet.setTitle(L"ツイート");
    guiTweet.addln(L"text", GUITextArea::Create(6, 24, 140, false));
    guiTweet.add(L"tweet", GUIButton::Create(L"ツイート"));
    guiTweet.setPos(40, 200);

    while (System::Update())
    {
        guiAuth.button(L"auth").enabled = (guiAuth.textField(L"PIN").text.length == 7);
        guiTweet.button(L"tweet").enabled = !guiTweet.textArea(L"text").text.isEmpty;

        if (guiAuth.button(L"auth").pushed && twitter.verifyPIN(guiAuth.textField(L"PIN").text))
        {
            guiAuth.release();
            guiTweet.textArea(L"text").enabled = true;
        }

        if (guiTweet.button(L"tweet").pushed)
        {
            twitter.tweet(guiTweet.textArea(L"text").text);
            guiTweet.textArea(L"text").setText(L"");
        }
    }
}

6. 3D Plot

# include <Siv3D.hpp>

void Plot3D(const ParsedExpression& f, MeshData& meshData)
{
	for (auto& v : meshData.vertices)
	{
		v.position.y = static_cast<float>(f.evaluateOpt({
			{ L"x", v.position.x },{ L"y", v.position.z } }).value_or(0.0));
	}

	meshData.computeNormals();
}

void Main()
{
	Window::Resize(800, 600);
	Graphics::SetBackground(Color(120, 180, 160));

	const String defaultExpression = L"5 * sin(sqrt(x^2+y^2)+0.0001) / (sqrt(x^2+y^2)+0.0001)";
	MeshData meshData = MeshData::Grid(25, 100);
	Plot3D(ParsedExpression(defaultExpression), meshData);
	DynamicMesh mesh(meshData);

	GUI gui(GUIStyle::Default);
	gui.addln(L"exp", GUITextArea::Create(2, 32));
	gui.add(L"grid", GUIToggleSwitch::Create(L"グリッド非表示", L"グリッド表示", false));
	gui.textArea(L"exp").setText(defaultExpression);

	while (System::Update())
	{
		if (!gui.textArea(L"exp").active)
		{
			Graphics3D::FreeCamera();
		}

		if (gui.textArea(L"exp").hasChanged)
		{
			if (const ParsedExpression exp{ gui.textArea(L"exp").text })
			{
				gui.textArea(L"exp").style.color = Palette::Black;
				Plot3D(exp, meshData);
				mesh.fillVertices(meshData.vertices);
			}
			else
			{
				gui.textArea(L"exp").style.color = Palette::Red;
			}
		}

		if (mesh)
		{
			if (gui.toggleSwitch(L"grid").isRight)
			{
				RasterizerState raster = RasterizerState::WireframeCullBack;
				raster.depthBias = 10000;
				Graphics3D::SetRasterizerState(raster);

				mesh.draw(Palette::Black);

				Graphics3D::SetRasterizerState(RasterizerState::Default3D);
			}

			mesh.draw();
		}
	}
}

7. 手書き文字認識

# include <Siv3D.hpp>

void Main()
{
	const HandwritingRecognizer recognizer(L"Example/Hiragana.model");
	const Font font(34);

	Image image(400, 400, Palette::White);

	DynamicTexture texture(image);

	Array<Array<Point>> pointsList;

	while (System::Update())
	{
		texture.draw();

		if (Input::MouseL.pressed)
		{
			if (Input::MouseL.clicked)
			{
				pointsList.emplace_back();
			}

			pointsList.back().push_back(Mouse::Pos());

			const Point from = Input::MouseL.clicked ? Mouse::Pos() : Mouse::PreviousPos();

			Line(from, Mouse::Pos()).overwrite(image, 8, Palette::Blue);

			texture.fill(image);
		}

		if (Input::MouseR.clicked)
		{
			pointsList.clear();

			image.fill(Palette::White);

			texture.fill(image);
		}

		const auto results = recognizer.recognize(image.size, pointsList);

		for (auto i : step(int(results.size())))
		{
			const String text = results[i].character;

			const double score = results[i].score;

			const Rect rect(440, 20 + i * 70, 120, 60);

			rect.draw(HSV(40, 1.0, Saturate(score + 1.0)));

			font(text).drawCenter(rect.center, Palette::Black);
		}
	}
}

8. Glitch

# include <Siv3D.hpp>

void Main()
{
	const Image image = Dialog::OpenImage();

	if (!image)
	{
		return;
	}

	const int32 noiseCount = image.num_pixels / 4000;
	const Array<uint8> original = image.encodeJPEG(90).asArray();

	DynamicTexture texture(image);
	Window::Resize(texture.size);

	while (System::Update())
	{
		if (Input::MouseL.clicked)
		{
			Array<uint8> memory(original);

			for (int32 i = 0; i < noiseCount; ++i)
			{
				memory[Random(630u, static_cast<uint32>(memory.size() - 1))] = static_cast<uint8>(Random(255));
			}

			texture.fill(Image(ByteArray(std::move(memory))));
		}

		texture.draw();
	}
}

スクリプト版 Siv3D 「SivScript(仮称)」

  • Visual Studio など C++ コンパイラが不要
  • 開発・実行環境のサイズは 20 MB 程度 (Visual Studio は 5 GB 以上)
  • Siv3D のほとんどの機能を、ほぼ同じコードで実行可能
  • ホットリロードにより、アプリを再起動せずコードを更新できる
  • 8 月末リリース予定

ペイント

void Main()
{
    Image image(Window::Size(), Palette::White);
    DynamicTexture texture(image);

    while (System::Update())
    {
        if (Input::MouseL.pressed)
        {
            Line(Mouse::PreviousPos(), Mouse::Pos())
				.overwrite(image, 8, Palette::Orange);

            texture.fill(image);
        }

        texture.draw();
    }
}

Say

void Main()
{
    Say("みなさんこんにちは。ここは東京です。");
	
    while (System::Update())
    {

    }
}

ブロックくずし

void Main()
{
	const Point blockSize(40, 20);
	Array<Rect> blocks;

	for (int y = 0; y < 5; ++y)
	{
		for (int x = 0; x <600 / blockSize.x; ++x)
		{
			blocks.push_back(
			 Rect(blockSize.x * x, 60 + blockSize.y * y, blockSize));
		}
	}

	const double speed = 8.0;
	Vec2 ballSpeed(0, -speed);
	Rect wall(60, 10);
	Circle ball(320, 420, 8) ;

	while (System::Update())
	{
		ball.moveBy(ballSpeed);
		wall.setCenter(Mouse::Pos().x, 480);

		for (uint i = 0; i < blocks.size(); ++i)
		{
			if (blocks[i].intersects(ball))
			{
				if (blocks[i].bottom.intersects(ball)
				 || blocks[i].top.intersects(ball))
				{
					ballSpeed.y *= -1;
				}
				else
				{
					ballSpeed.x *= -1;					
				}

				blocks.removeAt(i);
				break;
			}
		}

		for (uint i = 0; i < blocks.size(); ++i)
		{
			blocks[i].stretched(-1).draw(HSV(blocks[i].y - 40));
		}

		if (ball.y < 0 && ballSpeed.y < 0)
		{
			ballSpeed.y *= -1;
		}

		if ((ball.x < 0 && ballSpeed.x < 0)
		 || (600 < ball.x&& ballSpeed.x > 0))
		{
			ballSpeed.x *= -1;
		} 

		if (ballSpeed.y > 0 && wall.intersects(ball))
		{
			ballSpeed = Vec2((ball.x - wall.center.x) / 8,
			 -ballSpeed.y).setLength(speed);
		}

		ball.draw();
		wall.draw();
	}
}

おまけ

12 種類の方法で操作するブロックくずし

# include <Siv3D.hpp>
# include <Siv3DAddon/LeapMotion.hpp>

enum class InputType
{
	Mouse, Keyboard, Leap, XInput, Clap, Acceleration, Arduino, Kinect, Pentab, Touch, AR, FFT
};

Array<Rect> GetWalls(InputType type, double& rotation, Recorder& r, Webcam& w)
{
	const int W = Window::Width(), H = Window::Height();
	static Serial arduino(7);
	static int x = 180;
	Array<Rect> walls;

	rotation *= (type == InputType::AR);

	if (type == InputType::Mouse)
	{
		walls.push_back(Rect(200, 20).setCenter(Mouse::Pos().x, H - 100));
	}
	else if (type == InputType::Keyboard)
	{
		x += -24 * Input::KeyLeft.pressed + 24 * Input::KeyRight.pressed;
		walls.push_back(Rect(200, 20).setCenter(x, H - 100));
	}
	else if (type == InputType::Leap)
	{
		for (const auto& pointable : LeapMotion::Hands())
			walls.emplace_back(int(W / 2 + pointable.pos.x * 8), H - 100, 200, 20);
	}
	else if (type == InputType::XInput)
	{
		x += (XInput(0).leftThumbX / 1500) * (abs(XInput(0).leftThumbX) > 7000);
		walls.push_back(Rect(200, 20).setCenter(x, H - 100));
	}
	else if (type == InputType::Clap)
	{
		if (x == 10 && r.getMaxAmplitude() >= 0.2)
			x = W + 200;
		walls.push_back(Rect(x = Max(10, x - 40), 20).setCenter(W / 2, H - 100));
	}
	else if (type == InputType::Acceleration)
	{
		walls.push_back(Rect(300, 20).setCenter(W / 2 + int(KinectV1::GetAcceleration().x * (W + 200)), H - 100));
	}
	else if (type == InputType::Arduino)
	{
		uint8 data;
		if (arduino.available() && arduino.read(data))
			x = data / 256.0 * W;
		walls.emplace_back(x, H - 100, 200, 20);
	}
	else if (type == InputType::Kinect)
	{
		std::array<Optional<KinectV1Body>, 2> bodies;
		if (KinectV1::GetBodyFrame(bodies))
			bodies[0].then([&](KinectV1Body b) { x = (b.joints[V1JointType::Head].depthSpacePos.x - 320) * 6; });
		walls.push_back(Rect(400, 20).setCenter(x, H - 100));
	}
	else if (type == InputType::Pentab)
	{
		walls.push_back(Rect(200 + Pentablet::Pressure() * 300, 20).setCenter(Mouse::Pos().x, H - 100));
		rotation = Radians(Pentablet::DegreeY());
	}
	else if (type == InputType::Touch)
	{
		for (const auto& touch : Input::GetTouches())
			walls.emplace_back(Rect(200, 40).setCenter(touch.pos));
	}
	else if (type == InputType::AR)
	{
		Array<ARMarker> markers;

		if (w.getMarkers(markers) && !markers.empty())
		{
			x = static_cast<int>((1.0 - markers[0].screenPos.x / 640.0) *W*1.2 - W*0.2);
			rotation = -markers[0].rotation2D;
		}

		walls.emplace_back(x, H - 100, 250, 30);
	}
	else if (type == InputType::FFT)
	{
		const auto fft = FFT::Analyze(r);
		const auto it = std::max_element(&fft.buffer[100], &fft.buffer[360]);
		if (*it > 0.004)
			x = std::distance(&fft.buffer[100], it) * 6;
		walls.push_back(Rect(400, 20).setCenter(x, H - 100));
	}

	x = Clamp(x, 0, W);
	return walls;
}

Array<Rect> InitBlocks()
{
	Array<Rect> rects;
	const Size size(80, 40);
	const int w = Window::Width() / size.x;
	for (int i : step(5 * w))
		rects.emplace_back(i % w * size.x, 60 + i / w  * size.y, size);
	return rects;
}

void Main()
{
	Window::Resize(1600, 1000);
	Graphics::SetBackground(Palette::White);
	LeapMotion::RegisterAddon();
	Recorder recorder(5s, RecordingFormat::S44100, true);
	recorder.start();
	Webcam webcam(0);
	webcam.start();
	webcam.startAR();
	KinectV1::Start(KinectV1DataType::DefaultSeated);

	const Sound sound{ Wave(0.1s, [](double t) {return Fraction(t * 880)*0.5 - 0.25; }) };
	const double speed = 12.0;
	double rotation = 0.0, tone = 0;
	Circle ball(700, 400, 16);
	Vec2 ballVelocity(2.0, -speed);
	Array<Vec3> effects;
	Array<Rect> blocks = InitBlocks();
	GUI gui(GUIStyle::Default);
	gui.add(L"device", GUIRadioButton::Create({ L"マウス", L"キーボード", L"LeapMotion", L"Xbox 360",
		L"手拍子", L"加速度センサ", L"Arduino", L"Kinect", L"ペンタブ", L"マルチタッチ", L"AR マーカー", L"口笛" }, 0u));

	while (System::Update())
	{
		if (Input::KeyR.clicked)
			blocks = InitBlocks();

		for (auto it = blocks.begin(); it != blocks.end(); ++it)
		{
			if (it->intersects(ball))
			{
				if (it->bottom.intersects(ball) || it->top.intersects(ball))
					ballVelocity.y *= -1;
				else if (it->left.intersects(ball) || it->right.intersects(ball))
					ballVelocity.x *= -1;

				effects.emplace_back(Vec2(it->center), 0.0);
				blocks.erase(it);
				sound.playMulti(1, 1, Exp2(tone++ / 12.0));
				break;
			}
		}

		if ((ball.center.y<0 && ballVelocity.y<0) || (ball.y > Window::Height()))
			ballVelocity.y *= -1;

		if ((ball.center.x<0 && ballVelocity.x<0) || (Window::Width()<ball.center.x && ballVelocity.x>0))
			ballVelocity.x *= -1;

		for (auto& effect : effects)
		{
			const double value = Exp(-(effect.z += 0.01) * 5.0);
			Circle(effect.xy(), 400.0 * (1.0 - value)).drawFrame(value * 50, 0.0, ColorF(1.0, 0.8, 0.8));
		}

		for (const auto& block : blocks)
			block.draw(HSV((block.y - 40) * 1.2, 0.8, 1.0).toColorF(0.9));

		for (const auto& w : GetWalls(InputType(gui.radioButton(L"device").checkedItem.value()), rotation, recorder, webcam))
		{
			const auto wall = RoundRect(Rect(-w.size / 2, w.size), 6).asPolygon().rotated(rotation).moveBy(w.center);
			wall.draw(Palette::Skyblue);

			if (ballVelocity.y>0 && wall.intersects(ball))
			{
				ballVelocity = Vec2(Clamp((ball.center.x - w.center.x) / 8, -10., 10.)
					+ Random(-2., 2.), -ballVelocity.y).normalized()*speed;
				sound.playMulti(1, 1, 0.5);
			}
		}

		Erase_if(effects, [](const Vec3& effect) { return effect.z > 1.0; });
		tone = ball.y > Window::Height() * 0.6 ? 0 : tone;
		ball.moveBy(ballVelocity).draw(Palette::Skyblue);
		gui.style.visible = Input::KeySpace.pressed;
	}
}
⚠️ **GitHub.com Fallback** ⚠️