map<DotNode*, set<DotNode*> > SpatialNeighbors(DotGraph& g) { if (g.nodes.empty()) return map<DotNode*, set<DotNode*> >(); ////////////////////////// map<Point, DotNode*> posToNode; vector<Point> points; Rectangle bbRect(g.nodes[0]->getPos()); for (auto node : g.nodes) { Rectangle bb = node->getBoundingRectangle(); vector<Point> pp; pp.push_back(node->getPos()); pp.push_back(Point(bb.xl, bb.yl)); pp.push_back(Point(bb.xr, bb.yl)); pp.push_back(Point(bb.xl, bb.yr)); pp.push_back(Point(bb.xr, bb.yr)); for (auto point : pp) { points.push_back(point); posToNode[point] = node; bbRect.Add(point); } } //boundary double sz = min(bbRect.getWidth(), bbRect.getHeight()) * 0.1; bbRect.Add(bbRect.minPoint() - Point(sz, sz)); bbRect.Add(bbRect.maxPoint() + Point(sz, sz)); points.push_back(Point(bbRect.xl, bbRect.yl)); points.push_back(Point(bbRect.xl, bbRect.yr)); points.push_back(Point(bbRect.xr, bbRect.yl)); points.push_back(Point(bbRect.xr, bbRect.yr)); auto dt = geometry::DelaunayTriangulation::Create(points); map<DotNode*, set<DotNode*> > neighbors; for (auto seg : dt->getSegments()) { if (!posToNode.count(seg.first)) continue; if (!posToNode.count(seg.second)) continue; DotNode* an = posToNode[seg.first]; DotNode* bn = posToNode[seg.second]; if (an == bn) continue; neighbors[an].insert(bn); neighbors[bn].insert(an); } return neighbors; }
int __cdecl HookedPlayMovieSlide(const char* imageFile, const char* soundFile, const SubtitleLine* subtitles, int flags, int soundtrackId) { logger->info("Play Movie Slide {} {} {} {}", imageFile, soundFile, flags, soundtrackId); // Load img into memory using TIO unique_ptr<vector<uint8_t>> imgData(TioReadBinaryFile(imageFile)); if (!imgData) { logger->error("Unable to load the image file {}", imageFile); return 1; // Can't play because we cant load the file } auto device = graphics->device(); gfx::ImageFileInfo info; auto surface(gfx::LoadImageToSurface(graphics->device(), *imgData.get(), info)); movieFuncs.MovieIsPlaying = true; device->ShowCursor(FALSE); // Clear screen with black color and present immediately device->Clear(0, nullptr, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0, 0, 0); device->Present(nullptr, nullptr, nullptr, nullptr); SubtitleRenderer subtitleRenderer(subtitles); TigRect bbRect(0, 0, graphics->backBufferDesc().Width, graphics->backBufferDesc().Height); TigRect destRect(0, 0, info.width, info.height); destRect.FitInto(bbRect); RECT fitDestRect = destRect.ToRect(); Stopwatch sw; TigSoundStreamWrapper stream; if (soundFile) { if (!stream.Play(soundFile, TigSoundType::Voice)) { logger->error("Unable to play sound {} during slideshow.", soundFile); } else { stream.SetVolume(*tigSoundAddresses.movieVolume); } } bool keyPressed = false; while (!keyPressed && (!stream.IsValid() || stream.IsPlaying() || sw.GetElapsedMs() < 3000)) { D3DLOG(device->Clear(0, nullptr, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0, 0, 0)); D3DLOG(device->BeginScene()); D3DLOG(device->StretchRect(surface, NULL, graphics->backBuffer(), &fitDestRect, D3DTEXF_LINEAR)); subtitleRenderer.Render(); D3DLOG(device->EndScene()); D3DLOG(device->Present(NULL, NULL, NULL, NULL)); templeFuncs.ProcessSystemEvents(); TigMsg msg; while (!msgFuncs.Process(&msg)) { // Flags 1 seems to disable skip via keyboard. Also seems unused. if (!(flags & 1) && msg.type == TigMsgType::KEYSTATECHANGE && LOBYTE(msg.arg2) == 1) { // TODO Wait for the key to be unpressed again keyPressed = true; break; } } } movieFuncs.MovieIsPlaying = false; device->ShowCursor(TRUE); return 0; }