<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>小前端</title>
  
  <subtitle>前端开发 · AI探索 · 3D可视化</subtitle>
  <link href="https://guangze233.com/atom.xml" rel="self"/>
  
  <link href="https://guangze233.com/"/>
  <updated>2026-08-01T15:43:09.497Z</updated>
  <id>https://guangze233.com/</id>
  
  <author>
    <name>guangze233</name>
    
  </author>
  
  <generator uri="https://hexo.io/">Hexo</generator>
  
  <entry>
    <title>学习 langchain</title>
    <link href="https://guangze233.com/2025/03/10/learn-langchain/"/>
    <id>https://guangze233.com/2025/03/10/learn-langchain/</id>
    <published>2025-03-10T03:30:23.000Z</published>
    <updated>2026-08-01T15:43:09.497Z</updated>
    
    <content type="html"><![CDATA[<ol><li>学习 LangChain 的官网文档：<a href="https://python.langchain.com/docs/tutorials">https://python.langchain.com/docs/tutorials</a></li><li>仿照DeepSeek和通义千问页面，开发了一个简单的问答系统。<ul><li>体验 <a href='http://guangze233.com:10002' target="_blank">小前端助手</a></li></ul></li></ol><img src="/2025/03/10/learn-langchain/01.png" class="" width="300" height="320" title="问答页面"><span id="more"></span><h1 id="一、编程实战"><a href="#一、编程实战" class="headerlink" title="一、编程实战"></a>一、编程实战</h1><h2 id="设计"><a href="#设计" class="headerlink" title="设计"></a>设计</h2><h3 id="原型"><a href="#原型" class="headerlink" title="原型"></a>原型</h3><img src="/2025/03/10/learn-langchain/05.png" class="" width="560" height="640" title="页面原型"><h3 id="功能"><a href="#功能" class="headerlink" title="功能"></a>功能</h3><ul><li>使用本地大模型 –&gt; 部署时使用的deep官网api，体验页面额度用完为止</li><li>前端对话框流式输出 –&gt; 非流式，docker 部署后流式效果消失，未排查</li><li>单个对话场景中的多轮对话</li><li>输出内容使用 markdown 渲染</li></ul><h2 id="示例"><a href="#示例" class="headerlink" title="示例"></a>示例</h2><h3 id="前后端-流式输出"><a href="#前后端-流式输出" class="headerlink" title="前后端 + 流式输出"></a>前后端 + 流式输出</h3><blockquote><p>20250304</p></blockquote><video controls width="560" height="560">  <source src="./01.mp4" type="video/mp4" /></video><h3 id="多轮对话"><a href="#多轮对话" class="headerlink" title="多轮对话"></a>多轮对话</h3><blockquote><p>20250305、DeepSeek官网 API</p></blockquote><video controls width="560" height="560">  <source src="./02.mp4" type="video/mp4" /></video><h3 id="联网搜索"><a href="#联网搜索" class="headerlink" title="联网搜索"></a>联网搜索</h3><blockquote><p>20250306<br>function-call / tool-calling<br>回答速度变慢、 出现重复调用 tool-calling 的情况，待优化提示词</p></blockquote><img src="/2025/03/10/learn-langchain/04.png" class="" width="560" height="320" title="tool-calling"><h2 id="技术栈"><a href="#技术栈" class="headerlink" title="技术栈"></a>技术栈</h2><img src="/2025/03/10/learn-langchain/02.png" class="" width="560" height="320" title="系统结构"><h2 id="网站部署"><a href="#网站部署" class="headerlink" title="网站部署"></a>网站部署</h2><ul><li>部署 python 系统</li><li>nginx 代理需要关闭缓存，proxy_buffering off; <del>使用 docker 部署后流式输出失效了，不管了一股脑输出吧</del></li></ul><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><blockquote><p>20250628</p></blockquote><ol><li>学习了 langchain 的基础用法，借助学习langchain 了解了许多大模型相关概念，点亮了许多技能；</li><li>实际开发过程中，Java 生态更适合工程化落地，有一些优秀的框架和项目可参考：<ul><li><a href="https://docs.langchain4j.dev/"><strong>langchain4j</strong></a>：java 版本的langchain，版本快速迭代中。</li><li><a href="https://langchat.cn/"><strong>langchat</strong></a>：一个基于 langchain4j 框架功能完整的智能问答项目，推荐学习项目实战和设计思路。</li></ul></li></ol><h1 id="二、官网学习"><a href="#二、官网学习" class="headerlink" title="二、官网学习"></a>二、官网学习</h1><h2 id="编程环境"><a href="#编程环境" class="headerlink" title="编程环境"></a>编程环境</h2><blockquote><p>练习使用的环境，请实际环境调整</p></blockquote><ol><li>编程环境：anaconda、pycharm</li><li>模型环境：在线 - DeepSeek API；离线 - Ollama部署模型</li><li>中间件<ul><li>qdrant：部署向量数据库，对外提供服务。</li></ul></li><li>当前LangChain版本是 v0.3，感觉 v0.2 的文档更好理解</li></ol><h2 id="环境变量"><a href="#环境变量" class="headerlink" title="环境变量"></a>环境变量</h2><blockquote><p>方便管理环境变量</p></blockquote><ol><li>安装依赖<figure class="highlight cmake"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">pip <span class="keyword">install</span> python-dotenv</span><br></pre></td></tr></table></figure></li><li>新建 <em>.env</em> 文件<figure class="highlight ini"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># OLLAMA</span></span><br><span class="line"><span class="attr">OLLAMA_MODEL</span> = <span class="string">&quot;deepseek-r1:32b&quot;</span></span><br><span class="line"><span class="attr">OLLAMA_MODEL_TOOL_CALLING</span> = <span class="string">&quot;MFDoom/deepseek-r1-tool-calling:32b&quot;</span></span><br><span class="line"><span class="attr">OLLAMA_EMBEDDING_MODEL</span> = <span class="string">&quot;nomic-embed-text&quot;</span></span><br><span class="line"><span class="attr">OLLAMA_API_KEY</span>=<span class="string">&quot;SK-AAA&quot;</span></span><br><span class="line"><span class="attr">OLLAMA_BASE_URL</span>=<span class="string">&quot;http://172.25.65.161:11434&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># DeepSeek</span></span><br><span class="line"><span class="attr">DeepSeek_MODEL</span> = <span class="string">&quot;deepseek-chat&quot;</span></span><br><span class="line"><span class="attr">DeepSeek_API_KEY</span> = <span class="string">&quot;替换成 DeepSeek 上申请的 API KEY&quot;</span></span><br><span class="line"><span class="attr">DeepSeek_BASE_URL</span> = <span class="string">&quot;https://api.deepseek.com&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># qdrant</span></span><br><span class="line"><span class="attr">QDRANT_URL</span> = <span class="string">&quot;http://172.26.31.84:6333&quot;</span></span><br><span class="line"><span class="attr">QDRANT_COLLECTION_NAME</span> = <span class="string">&quot;qdrant_dev&quot;</span></span><br></pre></td></tr></table></figure></li><li>编写加载工具 <em>load_env</em><blockquote><p>也可在使用时直接粘贴这两行代码</p></blockquote></li></ol><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 加载环境变量</span></span><br><span class="line"><span class="keyword">from</span> dotenv <span class="keyword">import</span> load_dotenv</span><br><span class="line">load_dotenv()</span><br></pre></td></tr></table></figure><h2 id="Get-started"><a href="#Get-started" class="headerlink" title="Get started"></a>Get started</h2><h3 id="llm-chain"><a href="#llm-chain" class="headerlink" title="llm_chain"></a>llm_chain</h3><blockquote><ol><li>教程地址：<a href="https://python.langchain.com/docs/tutorials/llm_chain/">https://python.langchain.com/docs/tutorials/llm_chain/</a></li><li>教程目的：langchain的简单入门，使用 prompt template 和 chat models 实现一个翻译器。</li></ol></blockquote><ol><li><p>安装包</p><figure class="highlight cmake"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">pip <span class="keyword">install</span> langchain</span><br><span class="line">pip <span class="keyword">install</span> langchain-openai</span><br></pre></td></tr></table></figure></li><li><p>代码</p><ul><li>使用提示词工具类：提示词即字符串；llm的输入是纯字符串，chat llm 的输入是json结构，包含了role、content等字段，其中content是纯字符串；langchain封装了一系列提示词工具类；</li><li>初始化模型：通过 ollama 来访问本地部署的大模型；通过 DeepSeek API 来使用DeepSeek官方服务。</li></ul></li></ol><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Build a simple LLM application with chat models and prompt templates</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> load_env</span><br><span class="line"><span class="keyword">import</span> os</span><br><span class="line"></span><br><span class="line"><span class="keyword">from</span> langchain_ollama.chat_models <span class="keyword">import</span> ChatOllama</span><br><span class="line"><span class="keyword">from</span> langchain_core.prompts <span class="keyword">import</span> ChatPromptTemplate</span><br><span class="line"></span><br><span class="line"><span class="comment"># 方式一: 调用本地 ollama 模型</span></span><br><span class="line">ollama_model = ChatOllama(</span><br><span class="line">    model=os.getenv(<span class="string">&quot;OLLAMA_MODEL&quot;</span>),</span><br><span class="line">    base_url=os.getenv(<span class="string">&quot;OLLAMA_BASE_URL&quot;</span>),</span><br><span class="line">    temperature=<span class="number">0</span>,</span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 提示词模板</span></span><br><span class="line">system_template = <span class="string">&quot;Just translate the following from English into &#123;language&#125;&quot;</span></span><br><span class="line">prompt_template = ChatPromptTemplate.from_messages(</span><br><span class="line">    [(<span class="string">&quot;system&quot;</span>, system_template), (<span class="string">&quot;user&quot;</span>, <span class="string">&quot;&#123;text&#125;&quot;</span>)]</span><br><span class="line">)</span><br><span class="line">prompt = prompt_template.invoke(</span><br><span class="line">    &#123;<span class="string">&quot;language&quot;</span>: <span class="string">&quot;中文&quot;</span>, <span class="string">&quot;text&quot;</span>: <span class="string">&quot;Build a simple LLM application with chat models and prompt templates&quot;</span>&#125;)</span><br><span class="line"><span class="comment"># print(f&quot;---&gt; 提示词模板: &#123;prompt&#125;&quot;)</span></span><br><span class="line"><span class="built_in">print</span>(<span class="string">f&quot;---&gt; 调用本地 ollama 模型: <span class="subst">&#123;ollama_model.invoke(prompt).content&#125;</span>&quot;</span>)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment"># 方式二: 调用 DeepSeek API</span></span><br><span class="line"><span class="keyword">from</span> langchain_openai <span class="keyword">import</span> ChatOpenAI</span><br><span class="line">openai_model = ChatOpenAI(</span><br><span class="line">    model_name=os.getenv(<span class="string">&quot;DeepSeek_MODEL&quot;</span>),</span><br><span class="line">    openai_api_key=os.getenv(<span class="string">&quot;DeepSeek_API_KEY&quot;</span>),</span><br><span class="line">    openai_api_base=os.getenv(<span class="string">&quot;DeepSeek_BASE_URL&quot;</span>),</span><br><span class="line">    temperature=<span class="number">0</span></span><br><span class="line">)</span><br><span class="line"><span class="built_in">print</span>(<span class="string">f&quot;---&gt; 调用DeepSeek API: <span class="subst">&#123;openai_model.invoke(prompt).content&#125;</span>&quot;</span>)</span><br></pre></td></tr></table></figure><ol start="3"><li>输出<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_">---&gt; </span><span class="language-bash">调用本地 ollama 模型: &lt;think&gt;</span></span><br><span class="line">Alright, I need to help the user build a simple LLM application using chat models and prompt templates. First, I&#x27;ll outline the main components they&#x27;ll need: a chat model API like OpenAI&#x27;s GPT-3 or similar, and some way to handle prompts.</span><br><span class="line"></span><br><span class="line">I should start by explaining how to set up the environment. They&#x27;ll probably need Python, so I&#x27;ll suggest installing necessary libraries like `openai` for interacting with the API. Next, I&#x27;ll walk them through getting an API key from OpenAI and setting it up securely.</span><br><span class="line"></span><br><span class="line">Then, I&#x27;ll move on to creating prompt templates. It&#x27;s important to structure these clearly so that the model understands what&#x27;s being asked. Maybe provide examples of different types of prompts, like question-answering or summarization.</span><br><span class="line"></span><br><span class="line">After that, I&#x27;ll cover how to send requests to the API using the prepared prompts. I&#x27;ll include code snippets showing how to format the request and handle the response. It might be helpful to mention error handling here too, in case there are issues with the API call.</span><br><span class="line"></span><br><span class="line">I should also think about user interaction. How will they input their queries? Maybe through a simple command-line interface or a web form if they want to make it more interactive. I&#x27;ll suggest building a basic UI so users can easily interact with the application.</span><br><span class="line"></span><br><span class="line">Testing is another crucial step. I&#x27;ll advise them to test different prompts and scenarios to ensure the model responds as expected. They might need to tweak their prompts based on initial results to improve accuracy or relevance.</span><br><span class="line"></span><br><span class="line">Finally, I&#x27;ll touch on deployment options. If they want others to use their app, they could deploy it on a cloud platform like Heroku or AWS. Alternatively, if it&#x27;s just for personal use, running it locally might be sufficient.</span><br><span class="line"></span><br><span class="line">Throughout the process, I&#x27;ll make sure to keep the instructions clear and straightforward, avoiding overly technical jargon so that even someone new to LLMs can follow along easily.</span><br><span class="line">&lt;/think&gt;</span><br><span class="line"></span><br><span class="line">使用聊天模型和提示模板构建一个简单的LLM应用程序</span><br><span class="line"><span class="meta prompt_">---&gt; </span><span class="language-bash">调用DeepSeek API: 构建一个简单的LLM应用程序，使用聊天模型和提示模板。</span></span><br></pre></td></tr></table></figure></li></ol><h3 id="retrievers"><a href="#retrievers" class="headerlink" title="retrievers"></a>retrievers</h3><blockquote><ol><li>教程地址：<a href="https://python.langchain.com/docs/tutorials/retrievers/">https://python.langchain.com/docs/tutorials/retrievers/</a></li><li>教程目的：向量数据库检索，解析pdf文件 –&gt; 存入向量数据库 –&gt; 查询向量数据（直接查询、构造检索器查询）</li></ol></blockquote><ol><li>向量数据<br>qdrant 可使用 <em>docker-compose.yml</em> 部署，部署后先创建相关 collect_name 再执行代码<figure class="highlight nestedtext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="attribute">version</span><span class="punctuation">:</span> <span class="string">&#x27;3.1&#x27;</span></span><br><span class="line"><span class="attribute">services</span><span class="punctuation">:</span></span><br><span class="line">  <span class="attribute">qdrant</span><span class="punctuation">:</span></span><br><span class="line">      <span class="attribute">container_name</span><span class="punctuation">:</span> <span class="string">qdrant</span></span><br><span class="line">      <span class="attribute">image</span><span class="punctuation">:</span> <span class="string">&#x27;qdrant/qdrant&#x27;</span></span><br><span class="line">      <span class="attribute">restart</span><span class="punctuation">:</span> <span class="string">always</span></span><br><span class="line">      <span class="attribute">ports</span><span class="punctuation">:</span></span><br><span class="line">      <span class="bullet">-</span> <span class="string">&quot;6333:6333&quot;</span></span><br><span class="line">      <span class="bullet">-</span> <span class="string">&quot;6334:6334&quot;</span></span><br><span class="line">      <span class="attribute">volumes</span><span class="punctuation">:</span></span><br><span class="line">      <span class="bullet">-</span> <span class="string">./qdrant:/qdrant/storage</span></span><br></pre></td></tr></table></figure></li><li>代码<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Build a semantic search engine</span></span><br><span class="line"><span class="keyword">import</span> load_env</span><br><span class="line"><span class="keyword">import</span> os</span><br><span class="line"></span><br><span class="line"><span class="comment"># 加载PDF</span></span><br><span class="line"><span class="keyword">from</span> langchain_community.document_loaders <span class="keyword">import</span> PyPDFLoader</span><br><span class="line"><span class="keyword">from</span> langchain_text_splitters <span class="keyword">import</span> RecursiveCharacterTextSplitter</span><br><span class="line"></span><br><span class="line">file_path = <span class="string">&quot;./data/pdf/nke-10k-2023.pdf&quot;</span></span><br><span class="line">loader = PyPDFLoader(file_path)</span><br><span class="line">docs = loader.load()</span><br><span class="line"><span class="built_in">print</span>(<span class="string">&quot;\n========== Loading documents ==========&quot;</span>)</span><br><span class="line"><span class="built_in">print</span>(<span class="string">f&quot;docs:<span class="subst">&#123;<span class="built_in">len</span>(docs)&#125;</span>&quot;</span>)</span><br><span class="line"><span class="comment"># print(f&quot;&#123;docs[0].page_content[:200]&#125;\n&quot;)</span></span><br><span class="line"><span class="comment"># print(f&quot;metadata:&#123;docs[0].metadata&#125;&quot;)</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 递归分割</span></span><br><span class="line">text_splitter = RecursiveCharacterTextSplitter(</span><br><span class="line">    chunk_size=<span class="number">1000</span>, chunk_overlap=<span class="number">200</span>, add_start_index=<span class="literal">True</span></span><br><span class="line">)</span><br><span class="line">all_splits = text_splitter.split_documents(docs)</span><br><span class="line"><span class="built_in">print</span>(<span class="string">&quot;\n========== Splitting ==========&quot;</span>)</span><br><span class="line"><span class="built_in">print</span>(<span class="string">f&quot;all_splits : <span class="subst">&#123;<span class="built_in">len</span>(all_splits)&#125;</span>&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 向量数据库</span></span><br><span class="line"><span class="keyword">from</span> langchain_ollama <span class="keyword">import</span> OllamaEmbeddings</span><br><span class="line"><span class="keyword">from</span> langchain_qdrant <span class="keyword">import</span> QdrantVectorStore</span><br><span class="line"></span><br><span class="line">embeddings = OllamaEmbeddings(model=os.getenv(<span class="string">&quot;OLLAMA_EMBEDDING_MODEL&quot;</span>), base_url=os.getenv(<span class="string">&quot;OLLAMA_BASE_URL&quot;</span>))</span><br><span class="line">vector_store = QdrantVectorStore.from_existing_collection(</span><br><span class="line">    collection_name=os.getenv(<span class="string">&quot;QDRANT_COLLECTION_NAME&quot;</span>),</span><br><span class="line">    embedding=embeddings,</span><br><span class="line">    url=os.getenv(<span class="string">&quot;QDRANT_URL&quot;</span>),</span><br><span class="line">)</span><br><span class="line">ids = vector_store.add_documents(documents=all_splits)</span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(<span class="string">&quot;\n========== 向量搜索 ==========&quot;</span>)</span><br><span class="line">results = vector_store.similarity_search(</span><br><span class="line">    <span class="string">&quot;How many distribution centers does Nike have in the US?&quot;</span></span><br><span class="line">)</span><br><span class="line"><span class="built_in">print</span>(<span class="string">f&quot;usage 1: similarity_search: <span class="subst">&#123;results[<span class="number">0</span>]&#125;</span>&quot;</span>)</span><br><span class="line"></span><br><span class="line">results = vector_store.similarity_search_with_score(<span class="string">&quot;What was Nike&#x27;s revenue in 2023?&quot;</span>)</span><br><span class="line">doc, score = results[<span class="number">0</span>]</span><br><span class="line"><span class="built_in">print</span>(<span class="string">f&quot;\nusage 2: similarity_search_with_score: <span class="subst">&#123;score&#125;</span>&quot;</span>)</span><br><span class="line"></span><br><span class="line">embedding = embeddings.embed_query(<span class="string">&quot;How were Nike&#x27;s margins impacted in 2023?&quot;</span>)</span><br><span class="line"></span><br><span class="line">results = vector_store.similarity_search_by_vector(embedding)</span><br><span class="line"><span class="built_in">print</span>(<span class="string">f&quot;\nusage 3: similarity_search_by_vector: <span class="subst">&#123;results[<span class="number">0</span>]&#125;</span>&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 检索器</span></span><br><span class="line"><span class="comment"># from typing import List</span></span><br><span class="line"><span class="comment"># from langchain_core.documents import Document</span></span><br><span class="line"><span class="comment"># from langchain_core.runnables import chain</span></span><br><span class="line"><span class="comment">#</span></span><br><span class="line"><span class="comment">#</span></span><br><span class="line"><span class="comment"># @chain</span></span><br><span class="line"><span class="comment"># def retriever(query: str) -&gt; List[Document]:</span></span><br><span class="line"><span class="comment">#     return vector_store.similarity_search(query, k=1)</span></span><br><span class="line"><span class="comment">#</span></span><br><span class="line"><span class="comment">#</span></span><br><span class="line"><span class="comment"># print(&quot;\n========== 自定义检索 ==========&quot;)</span></span><br><span class="line"><span class="comment">#</span></span><br><span class="line"><span class="comment"># print(retriever.batch(</span></span><br><span class="line"><span class="comment">#     [</span></span><br><span class="line"><span class="comment">#         &quot;How many distribution centers does Nike have in the US?&quot;,</span></span><br><span class="line"><span class="comment">#         &quot;When was Nike incorporated?&quot;,</span></span><br><span class="line"><span class="comment">#     ],</span></span><br><span class="line"><span class="comment"># ))</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(<span class="string">&quot;\n========== 向量数据自带的检索 ==========&quot;</span>)</span><br><span class="line">retriever = vector_store.as_retriever(</span><br><span class="line">    search_type=<span class="string">&quot;similarity&quot;</span>,</span><br><span class="line">    search_kwargs=&#123;<span class="string">&quot;k&quot;</span>: <span class="number">1</span>&#125;,</span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(retriever.batch(</span><br><span class="line">    [</span><br><span class="line">        <span class="string">&quot;How many distribution centers does Nike have in the US?&quot;</span>,</span><br><span class="line">        <span class="string">&quot;When was Nike incorporated?&quot;</span>,</span><br><span class="line">    ],</span><br><span class="line">))</span><br></pre></td></tr></table></figure></li></ol><h3 id="classification"><a href="#classification" class="headerlink" title="classification"></a>classification</h3><blockquote><ol><li>教程地址：<a href="https://python.langchain.com/docs/tutorials/classification/">https://python.langchain.com/docs/tutorials/classification/</a></li><li>教程目的：结构化输出</li></ol></blockquote><ol><li><p>模型选择</p><ul><li>使用 ollama 调用本地 DeepSeek 或者 DeepSeek API 都无法完成该教程，换了个模型可以跑通： ollama + qwq</li><li>若需使用DeekSeek完成结构化输出，可参考DeepSeek 官网提供的 <a href="https://api-docs.deepseek.com/zh-cn/guides/json_mode">JSON Ouput 示例</a>。 </li></ul></li><li><p>代码</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Classify Text into Labels</span></span><br><span class="line"><span class="keyword">import</span> load_env</span><br><span class="line"><span class="keyword">import</span> os</span><br><span class="line"></span><br><span class="line"><span class="keyword">from</span> langchain_ollama.chat_models <span class="keyword">import</span> ChatOllama</span><br><span class="line"><span class="keyword">from</span> langchain_core.prompts <span class="keyword">import</span> ChatPromptTemplate</span><br><span class="line"><span class="keyword">from</span> pydantic <span class="keyword">import</span> BaseModel, Field</span><br><span class="line"></span><br><span class="line">tagging_prompt = ChatPromptTemplate.from_template(</span><br><span class="line">    <span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="string">    Extract the desired information from the following passage.</span></span><br><span class="line"><span class="string">    </span></span><br><span class="line"><span class="string">    Only extract the properties mentioned in the &#x27;Classification&#x27; function.</span></span><br><span class="line"><span class="string">    </span></span><br><span class="line"><span class="string">    Passage:</span></span><br><span class="line"><span class="string">    &#123;input&#125;</span></span><br><span class="line"><span class="string">    &quot;&quot;&quot;</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Classification</span>(<span class="title class_ inherited__">BaseModel</span>):</span><br><span class="line">    sentiment: <span class="built_in">str</span> = Field(description=<span class="string">&quot;The sentiment of the text&quot;</span>)</span><br><span class="line">    aggressiveness: <span class="built_in">int</span> = Field(</span><br><span class="line">        description=<span class="string">&quot;How aggressive the text is on a scale from 1 to 10&quot;</span></span><br><span class="line">    )</span><br><span class="line">    language: <span class="built_in">str</span> = Field(description=<span class="string">&quot;The language the text is written in&quot;</span>)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">model = ChatOllama(</span><br><span class="line">    model=<span class="string">&quot;qwq:latest&quot;</span>,</span><br><span class="line">    base_url=os.getenv(<span class="string">&quot;OLLAMA_BASE_URL&quot;</span>),</span><br><span class="line">    temperature=<span class="number">0</span>,</span><br><span class="line">).with_structured_output(Classification)</span><br><span class="line"></span><br><span class="line">inp = <span class="string">&quot;Estoy increiblemente contento de haberte conocido! Creo que seremos muy buenos amigos!&quot;</span></span><br><span class="line">prompt = tagging_prompt.invoke(&#123;<span class="string">&quot;input&quot;</span>: inp&#125;)</span><br><span class="line">response = model.invoke(prompt)</span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(<span class="string">&quot;\n========== QwQ 格式化输出 ==========&quot;</span>)</span><br><span class="line"><span class="built_in">print</span>(response)</span><br></pre></td></tr></table></figure></li></ol><h3 id="extraction"><a href="#extraction" class="headerlink" title="extraction"></a>extraction</h3><blockquote><ol><li>教程地址：<a href="https://python.langchain.com/docs/tutorials/extraction/">https://python.langchain.com/docs/tutorials/extraction/</a></li><li>教程目的：结构化抽取数据，并手动添加 tool-call 消息</li><li><strong>这个教程没有跑通</strong></li></ol></blockquote><h2 id="Orchestration"><a href="#Orchestration" class="headerlink" title="Orchestration"></a>Orchestration</h2><h3 id="chatbot"><a href="#chatbot" class="headerlink" title="chatbot"></a>chatbot</h3><blockquote><ol><li>教程地址：<a href="https://python.langchain.com/docs/tutorials/chatbot/">https://python.langchain.com/docs/tutorials/chatbot/</a></li><li>教程目的：聊天机器人示例</li></ol></blockquote><ol><li><p>计算 token 消耗</p><blockquote><p>参考 <a href="https://api-docs.deepseek.com/zh-cn/quick_start/token_usage">DeepSeek 文档 · 离线计算 Tokens 用量</a></p></blockquote></li><li><p>代码（LangChain）</p><blockquote><p>消息管理部分还没测试明白</p></blockquote></li></ol><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Build a Chatbot</span></span><br><span class="line"><span class="keyword">import</span> load_env</span><br><span class="line"><span class="keyword">import</span> os</span><br><span class="line"></span><br><span class="line"><span class="comment"># 创建模型</span></span><br><span class="line"><span class="keyword">from</span> langchain_openai <span class="keyword">import</span> ChatOpenAI</span><br><span class="line"></span><br><span class="line">model = ChatOpenAI(</span><br><span class="line">    model_name=os.getenv(<span class="string">&quot;DeepSeek_MODEL&quot;</span>),</span><br><span class="line">    openai_api_key=os.getenv(<span class="string">&quot;DeepSeek_API_KEY&quot;</span>),</span><br><span class="line">    openai_api_base=os.getenv(<span class="string">&quot;DeepSeek_BASE_URL&quot;</span>),</span><br><span class="line">    temperature=<span class="number">0</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 添加消息历史</span></span><br><span class="line"><span class="keyword">from</span> langchain_core.chat_history <span class="keyword">import</span> BaseChatMessageHistory, InMemoryChatMessageHistory</span><br><span class="line"><span class="keyword">from</span> langchain_core.runnables.history <span class="keyword">import</span> RunnableWithMessageHistory</span><br><span class="line"><span class="keyword">from</span> langchain_core.messages <span class="keyword">import</span> HumanMessage</span><br><span class="line"><span class="keyword">from</span> langchain_core.prompts <span class="keyword">import</span> ChatPromptTemplate, MessagesPlaceholder</span><br><span class="line"><span class="keyword">from</span> operator <span class="keyword">import</span> itemgetter</span><br><span class="line"><span class="keyword">from</span> langchain_core.runnables <span class="keyword">import</span> RunnablePassthrough</span><br><span class="line"></span><br><span class="line"><span class="comment"># 添加消息历史控制 -- 自定义 token 计算</span></span><br><span class="line"><span class="keyword">from</span> transformers <span class="keyword">import</span> AutoTokenizer</span><br><span class="line"><span class="keyword">from</span> typing <span class="keyword">import</span> <span class="type">Sequence</span>, <span class="type">List</span></span><br><span class="line"><span class="keyword">from</span> langchain_core.messages <span class="keyword">import</span> trim_messages, BaseMessage, AIMessage, SystemMessage, ToolMessage</span><br><span class="line"></span><br><span class="line">chat_tokenizer_dir = <span class="string">&quot;./chat_tokenizer_dir&quot;</span></span><br><span class="line">tokenizer = AutoTokenizer.from_pretrained(</span><br><span class="line">    chat_tokenizer_dir, trust_remote_code=<span class="literal">True</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">str_token_counter</span>(<span class="params">text: <span class="built_in">str</span></span>) -&gt; <span class="built_in">int</span>:</span><br><span class="line">    tokens = tokenizer.encode(text)</span><br><span class="line">    <span class="keyword">return</span> <span class="built_in">len</span>(tokens)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">tiktoken_counter</span>(<span class="params">messages: <span class="type">List</span>[BaseMessage]</span>) -&gt; <span class="built_in">int</span>:</span><br><span class="line">    num_tokens = <span class="number">3</span>  <span class="comment"># every reply is primed with &lt;|start|&gt;assistant&lt;|message|&gt;</span></span><br><span class="line">    tokens_per_message = <span class="number">3</span></span><br><span class="line">    tokens_per_name = <span class="number">1</span></span><br><span class="line">    <span class="keyword">for</span> msg <span class="keyword">in</span> messages:</span><br><span class="line">        <span class="keyword">if</span> <span class="built_in">isinstance</span>(msg, HumanMessage):</span><br><span class="line">            role = <span class="string">&quot;user&quot;</span></span><br><span class="line">        <span class="keyword">elif</span> <span class="built_in">isinstance</span>(msg, AIMessage):</span><br><span class="line">            role = <span class="string">&quot;assistant&quot;</span></span><br><span class="line">        <span class="keyword">elif</span> <span class="built_in">isinstance</span>(msg, ToolMessage):</span><br><span class="line">            role = <span class="string">&quot;tool&quot;</span></span><br><span class="line">        <span class="keyword">elif</span> <span class="built_in">isinstance</span>(msg, SystemMessage):</span><br><span class="line">            role = <span class="string">&quot;system&quot;</span></span><br><span class="line">        <span class="keyword">else</span>:</span><br><span class="line">            <span class="keyword">raise</span> ValueError(<span class="string">f&quot;Unsupported messages type <span class="subst">&#123;msg.__class__&#125;</span>&quot;</span>)</span><br><span class="line">        num_tokens += (</span><br><span class="line">                tokens_per_message</span><br><span class="line">                + str_token_counter(role)</span><br><span class="line">                + str_token_counter(msg.content)</span><br><span class="line">        )</span><br><span class="line">        <span class="keyword">if</span> msg.name:</span><br><span class="line">            num_tokens += tokens_per_name + str_token_counter(msg.name)</span><br><span class="line">    <span class="keyword">return</span> num_tokens</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">trimmer = trim_messages(</span><br><span class="line">    max_tokens=<span class="number">65</span>,</span><br><span class="line">    strategy=<span class="string">&quot;last&quot;</span>,</span><br><span class="line">    token_counter=tiktoken_counter,</span><br><span class="line">    include_system=<span class="literal">True</span>,</span><br><span class="line">    allow_partial=<span class="literal">False</span>,</span><br><span class="line">    start_on=<span class="string">&quot;human&quot;</span>,</span><br><span class="line">)</span><br><span class="line"></span><br><span class="line">store = &#123;&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">get_session_history</span>(<span class="params">session_id: <span class="built_in">str</span></span>) -&gt; BaseChatMessageHistory:</span><br><span class="line">    <span class="keyword">if</span> session_id <span class="keyword">not</span> <span class="keyword">in</span> store:</span><br><span class="line">        store[session_id] = InMemoryChatMessageHistory()</span><br><span class="line">    <span class="keyword">return</span> store[session_id]</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">config = &#123;<span class="string">&quot;configurable&quot;</span>: &#123;<span class="string">&quot;session_id&quot;</span>: <span class="string">&quot;abc2&quot;</span>&#125;&#125;</span><br><span class="line">prompt = ChatPromptTemplate.from_messages(</span><br><span class="line">    [</span><br><span class="line">        (</span><br><span class="line">            <span class="string">&quot;system&quot;</span>,</span><br><span class="line">            <span class="string">&quot;You are a helpful assistant. Answer all questions to the best of your ability in &#123;language&#125;.&quot;</span>,</span><br><span class="line">        ),</span><br><span class="line">        MessagesPlaceholder(variable_name=<span class="string">&quot;messages&quot;</span>),</span><br><span class="line">    ]</span><br><span class="line">)</span><br><span class="line"></span><br><span class="line">chain = (</span><br><span class="line">        RunnablePassthrough.assign(messages=itemgetter(<span class="string">&quot;messages&quot;</span>) | trimmer)</span><br><span class="line">        | prompt</span><br><span class="line">        | model</span><br><span class="line">)</span><br><span class="line"><span class="comment"># input_messages_key 用来置顶需要保存的聊天历史</span></span><br><span class="line">with_message_history = RunnableWithMessageHistory(chain, get_session_history, input_messages_key=<span class="string">&quot;messages&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 预设历史消息</span></span><br><span class="line">messages = [</span><br><span class="line">    SystemMessage(content=<span class="string">&quot;you&#x27;re a good assistant&quot;</span>),</span><br><span class="line">    HumanMessage(content=<span class="string">&quot;hi! I&#x27;m bob&quot;</span>),</span><br><span class="line">    AIMessage(content=<span class="string">&quot;hi!&quot;</span>),</span><br><span class="line">    HumanMessage(content=<span class="string">&quot;I like vanilla ice cream&quot;</span>),</span><br><span class="line">    AIMessage(content=<span class="string">&quot;nice&quot;</span>),</span><br><span class="line">    HumanMessage(content=<span class="string">&quot;whats 2 + 2&quot;</span>),</span><br><span class="line">    AIMessage(content=<span class="string">&quot;4&quot;</span>),</span><br><span class="line">    HumanMessage(content=<span class="string">&quot;thanks&quot;</span>),</span><br><span class="line">    AIMessage(content=<span class="string">&quot;no problem!&quot;</span>),</span><br><span class="line">    HumanMessage(content=<span class="string">&quot;having fun?&quot;</span>),</span><br><span class="line">    AIMessage(content=<span class="string">&quot;yes!&quot;</span>),</span><br><span class="line">]</span><br><span class="line">history = get_session_history(<span class="string">&quot;abc2&quot;</span>)</span><br><span class="line">history.add_messages(messages)</span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(<span class="string">&quot;\n========== Managing Conversation History ==========&quot;</span>)</span><br><span class="line"></span><br><span class="line">response = with_message_history.invoke(</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="string">&quot;messages&quot;</span>: [HumanMessage(content=<span class="string">&quot;whats my name?&quot;</span>)],</span><br><span class="line">        <span class="string">&quot;language&quot;</span>: <span class="string">&quot;English&quot;</span>,</span><br><span class="line">    &#125;,</span><br><span class="line">    config=config,</span><br><span class="line">)</span><br><span class="line"><span class="built_in">print</span>(<span class="string">f&quot;response 1: <span class="subst">&#123;response&#125;</span>&quot;</span>)</span><br><span class="line"></span><br><span class="line">response = with_message_history.invoke(</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="string">&quot;messages&quot;</span>: [HumanMessage(content=<span class="string">&quot;what math problem did i ask?&quot;</span>)],</span><br><span class="line">        <span class="string">&quot;language&quot;</span>: <span class="string">&quot;English&quot;</span>,</span><br><span class="line">    &#125;,</span><br><span class="line">    config=config,</span><br><span class="line">)</span><br><span class="line"><span class="built_in">print</span>(<span class="string">f&quot;response 2: <span class="subst">&#123;response&#125;</span>&quot;</span>)</span><br></pre></td></tr></table></figure><ol start="3"><li>代码（LangGraph）<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> abc <span class="keyword">import</span> ABC</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> load_env</span><br><span class="line"></span><br><span class="line"><span class="keyword">from</span> langchain_core.messages <span class="keyword">import</span> HumanMessage</span><br><span class="line"><span class="keyword">from</span> langchain_core.messages <span class="keyword">import</span> AIMessage</span><br><span class="line"></span><br><span class="line"><span class="keyword">from</span> langgraph.checkpoint.memory <span class="keyword">import</span> MemorySaver</span><br><span class="line"><span class="keyword">from</span> langgraph.graph <span class="keyword">import</span> START, MessagesState, StateGraph</span><br><span class="line"><span class="keyword">from</span> langchain_core.prompts <span class="keyword">import</span> ChatPromptTemplate, MessagesPlaceholder</span><br><span class="line"></span><br><span class="line"><span class="keyword">from</span> typing <span class="keyword">import</span> <span class="type">Sequence</span>, <span class="type">List</span></span><br><span class="line"><span class="keyword">from</span> langchain_core.messages <span class="keyword">import</span> BaseMessage</span><br><span class="line"><span class="keyword">from</span> langgraph.graph.message <span class="keyword">import</span> add_messages</span><br><span class="line"><span class="keyword">from</span> typing_extensions <span class="keyword">import</span> Annotated, TypedDict</span><br><span class="line"></span><br><span class="line"><span class="keyword">from</span> langchain_core.messages <span class="keyword">import</span> SystemMessage, trim_messages</span><br><span class="line"></span><br><span class="line"><span class="comment"># 加载模型</span></span><br><span class="line"><span class="keyword">from</span> langchain_ollama.chat_models <span class="keyword">import</span> ChatOllama</span><br><span class="line"><span class="keyword">import</span> os</span><br><span class="line"></span><br><span class="line">model = ChatOllama(</span><br><span class="line">    model=os.getenv(<span class="string">&quot;OLLAMA_MODEL&quot;</span>),</span><br><span class="line">    base_url=os.getenv(<span class="string">&quot;OLLAMA_BASE_URL&quot;</span>),</span><br><span class="line">    temperature=<span class="number">0</span>,</span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Managing Conversation History</span></span><br><span class="line"><span class="keyword">from</span> transformers <span class="keyword">import</span> AutoTokenizer</span><br><span class="line"><span class="keyword">from</span> langchain_core.messages <span class="keyword">import</span> BaseMessage, ToolMessage</span><br><span class="line"></span><br><span class="line">chat_tokenizer_dir = <span class="string">&quot;./chat_tokenizer_dir&quot;</span></span><br><span class="line">tokenizer = AutoTokenizer.from_pretrained(</span><br><span class="line">    chat_tokenizer_dir, trust_remote_code=<span class="literal">True</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">str_token_counter</span>(<span class="params">text: <span class="built_in">str</span></span>) -&gt; <span class="built_in">int</span>:</span><br><span class="line">    tokens = tokenizer.encode(text)</span><br><span class="line">    <span class="keyword">return</span> <span class="built_in">len</span>(tokens)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">tiktoken_counter</span>(<span class="params">messages: <span class="type">List</span>[BaseMessage]</span>) -&gt; <span class="built_in">int</span>:</span><br><span class="line">    num_tokens = <span class="number">3</span>  <span class="comment"># every reply is primed with &lt;|start|&gt;assistant&lt;|message|&gt;</span></span><br><span class="line">    tokens_per_message = <span class="number">3</span></span><br><span class="line">    tokens_per_name = <span class="number">1</span></span><br><span class="line">    <span class="keyword">for</span> msg <span class="keyword">in</span> messages:</span><br><span class="line">        <span class="keyword">if</span> <span class="built_in">isinstance</span>(msg, HumanMessage):</span><br><span class="line">            role = <span class="string">&quot;user&quot;</span></span><br><span class="line">        <span class="keyword">elif</span> <span class="built_in">isinstance</span>(msg, AIMessage):</span><br><span class="line">            role = <span class="string">&quot;assistant&quot;</span></span><br><span class="line">        <span class="keyword">elif</span> <span class="built_in">isinstance</span>(msg, ToolMessage):</span><br><span class="line">            role = <span class="string">&quot;tool&quot;</span></span><br><span class="line">        <span class="keyword">elif</span> <span class="built_in">isinstance</span>(msg, SystemMessage):</span><br><span class="line">            role = <span class="string">&quot;system&quot;</span></span><br><span class="line">        <span class="keyword">else</span>:</span><br><span class="line">            <span class="keyword">raise</span> ValueError(<span class="string">f&quot;Unsupported messages type <span class="subst">&#123;msg.__class__&#125;</span>&quot;</span>)</span><br><span class="line">        num_tokens += (</span><br><span class="line">                tokens_per_message</span><br><span class="line">                + str_token_counter(role)</span><br><span class="line">                + str_token_counter(msg.content)</span><br><span class="line">        )</span><br><span class="line">        <span class="keyword">if</span> msg.name:</span><br><span class="line">            num_tokens += tokens_per_name + str_token_counter(msg.name)</span><br><span class="line">    <span class="keyword">return</span> num_tokens</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">trimmer = trim_messages(</span><br><span class="line">    max_tokens=<span class="number">65</span>,</span><br><span class="line">    strategy=<span class="string">&quot;last&quot;</span>,</span><br><span class="line">    token_counter=tiktoken_counter,</span><br><span class="line">    include_system=<span class="literal">True</span>,</span><br><span class="line">    allow_partial=<span class="literal">False</span>,</span><br><span class="line">    start_on=<span class="string">&quot;human&quot;</span>,</span><br><span class="line">)</span><br><span class="line"></span><br><span class="line">messages = [</span><br><span class="line">    SystemMessage(content=<span class="string">&quot;you&#x27;re a good assistant&quot;</span>),</span><br><span class="line">    HumanMessage(content=<span class="string">&quot;hi! I&#x27;m bob&quot;</span>),</span><br><span class="line">    AIMessage(content=<span class="string">&quot;hi!&quot;</span>),</span><br><span class="line">    HumanMessage(content=<span class="string">&quot;I like vanilla ice cream&quot;</span>),</span><br><span class="line">    AIMessage(content=<span class="string">&quot;nice&quot;</span>),</span><br><span class="line">    HumanMessage(content=<span class="string">&quot;whats 2 + 2&quot;</span>),</span><br><span class="line">    AIMessage(content=<span class="string">&quot;4&quot;</span>),</span><br><span class="line">    HumanMessage(content=<span class="string">&quot;thanks&quot;</span>),</span><br><span class="line">    AIMessage(content=<span class="string">&quot;no problem!&quot;</span>),</span><br><span class="line">    HumanMessage(content=<span class="string">&quot;having fun?&quot;</span>),</span><br><span class="line">    AIMessage(content=<span class="string">&quot;yes!&quot;</span>),</span><br><span class="line">]</span><br><span class="line"><span class="built_in">print</span>(<span class="string">&quot;\n========== Managing Conversation History ==========&quot;</span>)</span><br><span class="line"><span class="built_in">print</span>(trimmer.invoke(messages))</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">State</span>(<span class="title class_ inherited__">TypedDict</span>):</span><br><span class="line">    messages: Annotated[<span class="type">Sequence</span>[BaseMessage], add_messages]</span><br><span class="line">    language: <span class="built_in">str</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">prompt_template = ChatPromptTemplate.from_messages(</span><br><span class="line">    [</span><br><span class="line">        (</span><br><span class="line">            <span class="string">&quot;system&quot;</span>,</span><br><span class="line">            <span class="string">&quot;You are a helpful assistant. Answer all questions to the best of your ability in &#123;language&#125;.&quot;</span>,</span><br><span class="line">        ),</span><br><span class="line">        MessagesPlaceholder(variable_name=<span class="string">&quot;messages&quot;</span>),</span><br><span class="line">    ]</span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">call_model</span>(<span class="params">state: State</span>):</span><br><span class="line">    trimmed_messages = trimmer.invoke(state[<span class="string">&quot;messages&quot;</span>])</span><br><span class="line">    prompt = prompt_template.invoke(</span><br><span class="line">        &#123;<span class="string">&quot;messages&quot;</span>: trimmed_messages, <span class="string">&quot;language&quot;</span>: state[<span class="string">&quot;language&quot;</span>]&#125;</span><br><span class="line">    )</span><br><span class="line">    response = model.invoke(prompt)</span><br><span class="line">    <span class="keyword">return</span> &#123;<span class="string">&quot;messages&quot;</span>: response&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">workflow = StateGraph(state_schema=State)</span><br><span class="line">workflow.add_edge(START, <span class="string">&quot;model&quot;</span>)</span><br><span class="line">workflow.add_node(<span class="string">&quot;model&quot;</span>, call_model)</span><br><span class="line">app = workflow.<span class="built_in">compile</span>(checkpointer=MemorySaver())</span><br><span class="line"></span><br><span class="line">config = &#123;<span class="string">&quot;configurable&quot;</span>: &#123;<span class="string">&quot;thread_id&quot;</span>: <span class="string">&quot;abc678&quot;</span>&#125;&#125;</span><br><span class="line">query = <span class="string">&quot;What math problem did I ask?&quot;</span></span><br><span class="line">language = <span class="string">&quot;English&quot;</span></span><br><span class="line"></span><br><span class="line">input_messages = messages + [HumanMessage(query)]</span><br><span class="line">output = app.invoke(</span><br><span class="line">    &#123;<span class="string">&quot;messages&quot;</span>: input_messages, <span class="string">&quot;language&quot;</span>: language&#125;,</span><br><span class="line">    config,</span><br><span class="line">)</span><br><span class="line">output[<span class="string">&quot;messages&quot;</span>][-<span class="number">1</span>].pretty_print()</span><br><span class="line"></span><br></pre></td></tr></table></figure></li></ol><h3 id="agents"><a href="#agents" class="headerlink" title="agents"></a>agents</h3><blockquote><ol><li>教程地址：<a href="https://python.langchain.com/docs/tutorials/agents/">https://python.langchain.com/docs/tutorials/agents/</a></li><li>教程目的：搜索Agent示例，使用 langchain 的 agent 模块实现</li></ol></blockquote>]]></content>
    
    
    <summary type="html">&lt;ol&gt;
&lt;li&gt;学习 LangChain 的官网文档：&lt;a href=&quot;https://python.langchain.com/docs/tutorials&quot;&gt;https://python.langchain.com/docs/tutorials&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;仿照DeepSeek和通义千问页面，开发了一个简单的问答系统。&lt;ul&gt;
&lt;li&gt;体验 &lt;a href=&#39;http://guangze233.com:10002&#39; target=&quot;_blank&quot;&gt;小前端助手&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;img src=&quot;/2025/03/10/learn-langchain/01.png&quot; class=&quot;&quot; width=&quot;300&quot; height=&quot;320&quot; title=&quot;问答页面&quot;&gt;</summary>
    
    
    
    <category term="教程学习" scheme="https://guangze233.com/categories/%E6%95%99%E7%A8%8B%E5%AD%A6%E4%B9%A0/"/>
    
    
    <category term="DeepSeek" scheme="https://guangze233.com/tags/DeepSeek/"/>
    
    <category term="LangChain" scheme="https://guangze233.com/tags/LangChain/"/>
    
    <category term="Python" scheme="https://guangze233.com/tags/Python/"/>
    
    <category term="AI" scheme="https://guangze233.com/tags/AI/"/>
    
    <category term="大模型" scheme="https://guangze233.com/tags/%E5%A4%A7%E6%A8%A1%E5%9E%8B/"/>
    
  </entry>
  
  <entry>
    <title>学习 YOLOV5</title>
    <link href="https://guangze233.com/2025/02/14/YOLOV5/"/>
    <id>https://guangze233.com/2025/02/14/YOLOV5/</id>
    <published>2025-02-14T03:34:07.000Z</published>
    <updated>2026-08-01T15:43:12.898Z</updated>
    
    <content type="html"><![CDATA[<img src="/2025/02/14/YOLOV5/001.jpg" class="" width="160" height="160" title="YOLOV5"><p>学习使用 <a href="https://github.com/ultralytics/yolov5">YOLOV5</a>，识别动漫人物 “小小怪”。</p><span id="more"></span><h2 id="基础"><a href="#基础" class="headerlink" title="基础"></a>基础</h2><ul><li><a href="https://iheadwater.github.io/iheadwater_hackweek_tutorials/intro.html">大连理工大学 · 水资源科研编程入门教程</a>：速览 PyTorch/深度学习</li><li><a href="https://www.bilibili.com/video/BV1hE411t7RN/?spm_id_from=333.999.0.0&vd_source=1e15750346a6b797fe55bee7e478af4c">PyTorch深度学习快速入门教程（绝对通俗易懂！）【小土堆】</a>：环境安装、了解基础概念、照着敲代码快速反馈。</li><li><a href="https://www.bilibili.com/video/BV1tf4y1t7ru/?spm_id_from=333.1387.homepage.video_card.click&vd_source=1e15750346a6b797fe55bee7e478af4c">目标检测 YOLOv5 开源代码项目调试与讲解实战【土堆 x 布尔艺数】</a>：使用YOLOV5，教程使用 <em>yolov5-5.0</em>，实际操作时使用最新版本可以避免三方库的版本冲突问题。</li><li><a href="https://www.bilibili.com/video/BV1Y7411d7Ys?vd_source=1e15750346a6b797fe55bee7e478af4c">《PyTorch深度学习实践》完结合集</a>：完整课程，还没看完</li></ul><h2 id="实操"><a href="#实操" class="headerlink" title="实操"></a>实操</h2><blockquote><p>参考 <a href="https://docs.ultralytics.com/yolov5/tutorials/train_custom_data/">YOLOV5 README · 自定义数据训练</a></p></blockquote><ol><li>搜集图片数据：百度图片搜索“小小怪”，选择保存六张图片。</li><li>使用 <a href="https://app.roboflow.com/project-onrlv">Roboflow</a> 对原始图片打标，参考 <a href="https://blog.csdn.net/m0_67241828/article/details/130356928">Roboflow标注平台使用—-小白都能看懂</a></li><li>将 <strong>Roboflow</strong> 导出的数据放入系统中，修改配置文件并增加训练轮次，使用 train.py 训练。<em>修改方式参照 目标检测 YOLOv5 开源代码项目调试与讲解实战【土堆 x 布尔艺数】</em> <figure class="highlight stylus"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">python train<span class="selector-class">.py</span> <span class="attr">--data</span> coco128<span class="selector-class">.yaml</span> <span class="attr">--epochs</span> <span class="number">300</span> <span class="attr">--batch-size</span> -<span class="number">1</span></span><br></pre></td></tr></table></figure></li><li>将训练数据和新的测试数据放入 data/images，使用 detect.py 检测。</li></ol><h2 id="效果"><a href="#效果" class="headerlink" title="效果"></a>效果</h2><img src="/2025/02/14/YOLOV5/001.jpg" class="" width="160" height="160" title="001.jpg"><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><ol><li>训练100轮时，执行检测没有效果；改成300轮或降低检测标准才有反应。</li><li>目前是本地使用，未来<strong>将模型集成至web系统中调用</strong>。</li></ol>]]></content>
    
    
    <summary type="html">&lt;img src=&quot;/2025/02/14/YOLOV5/001.jpg&quot; class=&quot;&quot; width=&quot;160&quot; height=&quot;160&quot; title=&quot;YOLOV5&quot;&gt;

&lt;p&gt;学习使用 &lt;a href=&quot;https://github.com/ultralytics/yolov5&quot;&gt;YOLOV5&lt;/a&gt;，识别动漫人物 “小小怪”。&lt;/p&gt;</summary>
    
    
    
    <category term="教程学习" scheme="https://guangze233.com/categories/%E6%95%99%E7%A8%8B%E5%AD%A6%E4%B9%A0/"/>
    
    
    <category term="YOLOV5" scheme="https://guangze233.com/tags/YOLOV5/"/>
    
    <category term="PyTorch" scheme="https://guangze233.com/tags/PyTorch/"/>
    
    <category term="目标检测" scheme="https://guangze233.com/tags/%E7%9B%AE%E6%A0%87%E6%A3%80%E6%B5%8B/"/>
    
    <category term="深度学习" scheme="https://guangze233.com/tags/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0/"/>
    
  </entry>
  
  <entry>
    <title>学习 figma</title>
    <link href="https://guangze233.com/2024/08/16/figma-0/"/>
    <id>https://guangze233.com/2024/08/16/figma-0/</id>
    <published>2024-08-16T03:09:22.000Z</published>
    <updated>2026-08-01T15:43:21.427Z</updated>
    
    <content type="html"><![CDATA[<p>Figma 练习案例</p><blockquote><p><a href="https://www.bilibili.com/video/BV1fg411G7cs/?p=2&vd_source=1e15750346a6b797fe55bee7e478af4c">教程 · 零基础学 Figma【酸梅干超人出品】</a></p></blockquote><span id="more"></span><h1 id="零基础学-Figma【酸梅干超人出品】"><a href="#零基础学-Figma【酸梅干超人出品】" class="headerlink" title="零基础学 Figma【酸梅干超人出品】"></a>零基础学 Figma【酸梅干超人出品】</h1><h2 id="苹果商店页设计"><a href="#苹果商店页设计" class="headerlink" title="苹果商店页设计"></a>苹果商店页设计</h2><div style="display: flex;justify-content: space-evenly;">    <div style="text-align: center;">        <img src="/2024/08/16/figma-0/01-1.webp" class="" width="375" height="812">        <span>Today</span>    </div>    <div style="text-align: center;">        <img src="/2024/08/16/figma-0/01-2.webp" class="" width="375" height="812">        <span>App</span>    </div></div><p>知识点</p><ul><li>安装字体：<a href="https://jingyan.baidu.com/article/ad310e80292b0a5949f49eda.html">win11 本地安装字体</a>，重启 figma 自动加载。</li><li>安装插件：在插件中心搜索、安装；在 figma 画板中右键使用插件；头像插件–<strong>Avatar</strong></li><li>操作：<ul><li>显隐网格线：<strong>Shift+R</strong></li><li>查看间距：选中元素 –&gt; 按住 Alt 键, 鼠标移动到附近元素上（类似蓝湖）</li><li>像素级移动：选中元素 –&gt; 方向键每次移动 1 像素；选中元素 –&gt; 按住 Shift键，方向键每次移动 10 像素</li><li>蒙版：<strong>Ctrl+Alt+M</strong>，<em>案例中复制出的矩形蒙版会遮住原有矩形的边框效果，需要吧蒙版的边框效果再设置一遍</em></li><li>选中对齐：按住 Shift 键多选，在<strong>同一层级</strong>可快速选择（所选元素外围出现一个选择框，设置“获取”按钮对齐时，检查下粘贴进来的 Icon 层级）</li></ul></li><li>遗留点：<ul><li><strong>底部导航栏磨砂效果</strong>（B站弹幕）</li></ul></li></ul><p>练习源文件：<a href="/assets/figma-0/01.fig">教程1 - 苹果商店页.fig</a></p><h2 id="线性图标设计"><a href="#线性图标设计" class="headerlink" title="线性图标设计"></a>线性图标设计</h2><div style="display: flex;justify-content: space-evenly;">    <div> <img src="/2024/08/16/figma-0/02.webp" class="" width="375" height="812"> </div>    <div>         <img src="/2024/08/16/figma-0/02-01.png" class="" width="24" height="24" title="离线缓存">        <img src="/2024/08/16/figma-0/02-02.png" class="" width="24" height="24" title="历史记录">        <img src="/2024/08/16/figma-0/02-03.png" class="" width="24" height="24" title="我的收藏">        <img src="/2024/08/16/figma-0/02-04.png" class="" width="24" height="24" title="我的关注">        <img src="/2024/08/16/figma-0/02-05.png" class="" width="24" height="24" title="稍后再看">        <img src="/2024/08/16/figma-0/02-06.png" class="" width="24" height="24" title="B币钱包">    </div></div><p>知识点</p><ul><li><p>每个图标一个画布，方便统一对齐；图标画布可水平靠近，方便对比图表大小是否一致</p></li><li><p>图标的线宽、圆角等基本单位可设置为1.5，0.5 步长</p></li><li><p>节点操作：</p><ul><li>非闭合路径的描边在中间（ <strong>center</strong> ）</li><li>双击闭合路径展示可操作节点（支持拖拽、删除）</li><li>图形描边设置为<strong>center</strong>也便于调整大小</li></ul></li><li><p>图层操作</p><blockquote><p>摘抄自<a href="https://tongyi.aliyun.com/qianwen/">通义灵千问</a></p></blockquote><ul><li>Union Selection (<strong>合并选择</strong>)：将多个选定的对象合并为一个对象，并且保留各个对象的独立属性（如填充、描边等）。如果这些对象有重叠的部分，它们会合并成一个新的形状。</li><li>Flatten Selection (<strong>扁平化选择</strong>)：将多个选定的对象合并为一个<strong>单一的对象</strong>，并且删除所有原始对象的独立属性。这意味着所有的形状会被合并成一个单一的形状，任何填充或描边都会被合并后的形状继承。</li></ul></li><li><p>通过基础几何交并，制作复杂图形：心形 – 两个基础长方形的交并操作</p></li></ul><p>练习源文件：<a href="/assets/figma-0/02.fig">教程2 - 线性图标设计.fig</a></p><h2 id="面性图标设计"><a href="#面性图标设计" class="headerlink" title="面性图标设计"></a>面性图标设计</h2><div style="display: flex;justify-content: space-evenly;">    <div> <img src="/2024/08/16/figma-0/03.webp" class="" width="375" height="812"> </div>    <div>         <img src="/2024/08/16/figma-0/03-01.png" class="" width="44" height="44" title="创作首页">        <img src="/2024/08/16/figma-0/03-02.png" class="" width="44" height="44" title="稿件管理">        <img src="/2024/08/16/figma-0/03-03.png" class="" width="44" height="44" title="创作激励">        <img src="/2024/08/16/figma-0/03-04.png" class="" width="44" height="44" title="热门活动">    </div></div><p>知识点</p><ul><li>面性图标可由背景+图标组件构成，制作一组图标时，可先制作一个完整的图标，复制粘贴通用样式（背景大小、渐近色、阴影等）</li><li>弯曲工具（Bend tool）：选择弯曲工具，点击锚点可将普通锚点变为弯曲锚点（普通选中时有两个手柄）；选中弯曲手柄，再按住 <strong>Alt</strong> 键，可单独拖拽。</li><li>渐近色：可先设置起始终止颜色相同，稍稍改变色系来生成较为美观的渐近色</li></ul><p>练习源文件：<a href="/assets/figma-0/03.fig">教程3 - 面性图标设计.fig</a></p><h2 id="玻璃拟态页面设计"><a href="#玻璃拟态页面设计" class="headerlink" title="玻璃拟态页面设计"></a>玻璃拟态页面设计</h2><div style="display: flex;justify-content: space-evenly;">    <div style="text-align: center;">        <img src="/2024/08/16/figma-0/04-01.webp" class="" width="375" height="812">        <span>首页</span>    </div>    <div style="text-align: center;">        <img src="/2024/08/16/figma-0/04-02.webp" class="" width="375" height="812">        <span>我的</span>    </div></div><p>知识点</p><ul><li>玻璃拟态<ul><li>【&cross;】图层模糊：图片模糊 + 表面白色遮盖，需要同时修改两个图层，较为麻烦</li><li>【&check;】背景模糊：背景图层 + 模糊图层（设置背景模糊 + fill 透明度）</li><li>增强玻璃效果：边框渐变、圆角、阴影</li></ul></li><li>自动布局：<strong>Auto layout</strong>可实现 padding 效果；案例“我的-按分类搜索”中需将背景和文字编组，对编组施加 <strong>Auto layout</strong></li><li>属性复制/粘贴：Figma支持元素复制粘贴元素属性，选中元素后右键 <strong>Copy/Paste as -&gt; Copy as properties</strong></li><li><strong>素材包中已提供图标，无需自己制作</strong></li></ul><p>练习源文件：<a href="/assets/figma-0/04.fig">教程4 - 玻璃拟态页面设计.fig</a></p><h2 id="样式组件功能入门"><a href="#样式组件功能入门" class="headerlink" title="样式组件功能入门"></a>样式组件功能入门</h2><div style="display: grid;grid-template-columns:repeat(auto-fill, 375px);gap: 20px 5px;">    <img src="/2024/08/16/figma-0/05-01.webp" class="" width="375" height="812">    <img src="/2024/08/16/figma-0/05-02.webp" class="" width="375" height="812">    <img src="/2024/08/16/figma-0/05-03.webp" class="" width="375" height="812">    <img src="/2024/08/16/figma-0/05-04.webp" class="" width="375" height="812">    <img src="/2024/08/16/figma-0/05-05.webp" class="" width="375" height="812">    <img src="/2024/08/16/figma-0/05-06.webp" class="" width="375" height="812">    <img src="/2024/08/16/figma-0/05-07.webp" class="" width="375" height="812">    <img src="/2024/08/16/figma-0/05-08.webp" class="" width="375" height="812"></div><img src="/2024/08/16/figma-0/05-09.webp" class="" width="813" height="913"><p>知识点</p><ul><li>样式变量：颜色、文字、描边、阴影等都可以创建样式变量，方便复用</li><li>组件<ul><li>创建组件：选中元素可以创建组件，创建后的组件可在Assets中查看、删除、拖拽进画布</li><li>使用组件：复制组件模板，粘贴到画布中，复制后的元素未子元素，单向影响（修改父组件影响子元素，修改子元素不影响父组件）</li><li>使用经验：将创建的组件模板至于最左侧的一个画布中，便于区分、管理、查找组件。</li></ul></li></ul><p>练习源文件：<a href="/assets/figma-0/05.fig">教程5 - 样式组件功能入门.fig</a></p><h2 id="连线动画效果说明"><a href="#连线动画效果说明" class="headerlink" title="连线动画效果说明"></a>连线动画效果说明</h2><p>知识点</p><ul><li>页面切换：右侧抽屉顶部切换到<strong>Prototype</strong>，从元素的加号图标处拖出引导线，拖拽到目标元素处，即可实现页面切换效果。</li><li>切换效果：单击引导线可设置交互动效，如触发事件、目标元素、动画效果等。</li><li>动作：<ul><li><strong>Navigate to</strong>: 跳转页面，类似 Android 的可视化 Navigation</li><li><strong>Back</strong>: 返回上一级（好处是不用指明目标元素）</li><li><strong>overlay</strong>: 可实现打开弹框（<em>Open overlay</em>）、替换弹框（<em>Swap overlay</em>）、关闭弹框（<em>Close overlay</em>）</li><li><strong>Scoll to</strong>: 滚动到当前页面中的某个元素（锚点），需要先在页面中设置滚动元素（<em>Frame select</em> –&gt; <em>Overflow scrolling</em>）。</li></ul></li><li>动画：<ul><li><strong>Instant</strong>: 无效果，常用于底部导航栏的切换。</li><li><strong>Disslove</strong>: 淡隐淡出，常搭配 <em>Easy in and out</em></li><li><strong>Slide in/out</strong>：滑入滑出，常用于页面级切换。</li><li><strong>Smart Animate</strong>: 自动拟合动画，前提是两个元素的图层结构相同（A -&gt; B, 自动拟合出中间状态）。</li></ul></li></ul><p><em>备注：这个案例没有源文件</em></p><h2 id="单屏网页设计"><a href="#单屏网页设计" class="headerlink" title="单屏网页设计"></a>单屏网页设计</h2><blockquote><p>参考线和栅格应用</p></blockquote><img src="/2024/08/16/figma-0/07.webp" class="" width="1280" height="720"><p>知识点</p><ul><li>参考线和栅格的作用：辅助设计，保证系统的视觉一致性；若视觉效果查，可随机处置。</li><li>网页设计的画布一般不会直接用 1920*1080 尺寸，而是用小尺寸（如 1280 * 720）设计，好处是小尺寸可以向上兼容大尺寸。</li><li>设计网页时，可先设置栅格参数，之后不用纠结于元素的大小/距离公式<ul><li>设置栅格：<strong>Layout grid</strong></li><li>栅格参数：<strong>Columns</strong>、<strong>Type（默认 Strech 即可）</strong>，其他属性类似 css 的 grid 布局。</li></ul></li><li>快捷键：<strong>Shift + G</strong></li></ul><p>练习源文件：<a href="/assets/figma-0/07.fig">教程7 - 参考线和栅格应用.fig</a></p><h2 id="响应式网页设计"><a href="#响应式网页设计" class="headerlink" title="响应式网页设计"></a>响应式网页设计</h2><blockquote><p>教程视频很长，但是基本上没看懂（中断比较多）</p></blockquote><img src="/2024/08/16/figma-0/08.webp" class="" width="1280" height="3517"><p>知识点</p><ul><li>Frame、Auto Layout、Resize</li></ul><p>练习源文件：<a href="/assets/figma-0/08.fig">教程8 - 响应式网页设计.fig</a></p><h2 id="B端表格页设计"><a href="#B端表格页设计" class="headerlink" title="B端表格页设计"></a>B端表格页设计</h2><blockquote><p>没看懂，像是自动布局的应用。类似 css 盒子模型 + flex</p></blockquote><h2 id="B端规范搭建"><a href="#B端规范搭建" class="headerlink" title="B端规范搭建"></a>B端规范搭建</h2><p>知识点</p><ul><li>Variants 好用，组件切换。</li></ul>]]></content>
    
    
    <summary type="html">&lt;p&gt;Figma 练习案例&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;a href=&quot;https://www.bilibili.com/video/BV1fg411G7cs/?p=2&amp;vd_source=1e15750346a6b797fe55bee7e478af4c&quot;&gt;教程 · 零基础学 Figma【酸梅干超人出品】&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;</summary>
    
    
    
    <category term="教程学习" scheme="https://guangze233.com/categories/%E6%95%99%E7%A8%8B%E5%AD%A6%E4%B9%A0/"/>
    
    
    <category term="Figma" scheme="https://guangze233.com/tags/Figma/"/>
    
    <category term="UI设计" scheme="https://guangze233.com/tags/UI%E8%AE%BE%E8%AE%A1/"/>
    
    <category term="图标设计" scheme="https://guangze233.com/tags/%E5%9B%BE%E6%A0%87%E8%AE%BE%E8%AE%A1/"/>
    
    <category term="响应式设计" scheme="https://guangze233.com/tags/%E5%93%8D%E5%BA%94%E5%BC%8F%E8%AE%BE%E8%AE%A1/"/>
    
  </entry>
  
  <entry>
    <title>三维地形重建</title>
    <link href="https://guangze233.com/2024/07/06/%E4%B8%89%E7%BB%B4%E5%9C%B0%E5%BD%A2%E9%87%8D%E5%BB%BA/"/>
    <id>https://guangze233.com/2024/07/06/%E4%B8%89%E7%BB%B4%E5%9C%B0%E5%BD%A2%E9%87%8D%E5%BB%BA/</id>
    <published>2024-07-06T05:15:52.000Z</published>
    <updated>2026-08-01T15:43:18.526Z</updated>
    
    <content type="html"><![CDATA[<p>基于 Three.js 的真实三维地形可视化设计与实现。</p><img src="/2024/07/06/%E4%B8%89%E7%BB%B4%E5%9C%B0%E5%BD%A2%E9%87%8D%E5%BB%BA/01.png" class="" width="320" height="320" title="地形重建"><span id="more"></span><h1 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h1><blockquote><p>记录一次奇怪的需求开发。</p></blockquote><p><strong>需求</strong></p><ol><li>根据海底高程数据，重建海底三维地形；</li><li>根据多时期的高程数据，制作地形冲刷的动态视频。</li></ol><p><strong>原始数据格式</strong><br>一个txt文件，每行三列，数据含义未知。</p><h1 id="数据分析"><a href="#数据分析" class="headerlink" title="数据分析"></a>数据分析</h1><ol><li>假设数据含义：x - 平面坐标, y - 平面坐标, z - 海拔高度</li><li>点云展示</li><li>数据顺序规律</li><li>高程极值分布、统计规律<br>…</li></ol><h1 id="实现策略"><a href="#实现策略" class="headerlink" title="实现策略"></a>实现策略</h1><blockquote><p>回填启发：《基于 Three.js 的真实三维地形可视化设计与实现 – 测绘与空间地理信息 Vo1.38，No.10》</p></blockquote><ol><li>假设：threejs 基于 webgl实现，那么 threejs 的平面类一定也会把 z 轴信息传递给 webgl。</li><li>将栅格化的数据回填到Threejs的平面类中，并把高程通过 threejs 塞给 webgl，实现建面任务。</li></ol>]]></content>
    
    
    <summary type="html">&lt;p&gt;基于 Three.js 的真实三维地形可视化设计与实现。&lt;/p&gt;
&lt;img src=&quot;/2024/07/06/%E4%B8%89%E7%BB%B4%E5%9C%B0%E5%BD%A2%E9%87%8D%E5%BB%BA/01.png&quot; class=&quot;&quot; width=&quot;320&quot; height=&quot;320&quot; title=&quot;地形重建&quot;&gt;</summary>
    
    
    
    <category term="Threejs" scheme="https://guangze233.com/categories/Threejs/"/>
    
    
    <category term="Three.js" scheme="https://guangze233.com/tags/Three-js/"/>
    
    <category term="WebGL" scheme="https://guangze233.com/tags/WebGL/"/>
    
    <category term="3D可视化" scheme="https://guangze233.com/tags/3D%E5%8F%AF%E8%A7%86%E5%8C%96/"/>
    
    <category term="地形重建" scheme="https://guangze233.com/tags/%E5%9C%B0%E5%BD%A2%E9%87%8D%E5%BB%BA/"/>
    
  </entry>
  
  <entry>
    <title>学习 Blender</title>
    <link href="https://guangze233.com/2024/02/28/Blender-%E9%BB%91%E4%BD%93%E9%AA%91%E5%A3%AB%E2%85%A1/"/>
    <id>https://guangze233.com/2024/02/28/Blender-%E9%BB%91%E4%BD%93%E9%AA%91%E5%A3%AB%E2%85%A1/</id>
    <published>2024-02-28T12:52:10.000Z</published>
    <updated>2026-08-01T15:43:27.630Z</updated>
    
    <content type="html"><![CDATA[<p>Blender 练习案例</p><blockquote><p><a href="https://www.bilibili.com/video/BV1zh411Y7LX?p=29&spm_id_from=pageDriver&vd_source=1e15750346a6b797fe55bee7e478af4c">【合集8.21已更新93话】Blender 2.9-3.4黑铁骑士Ⅱ系统零基础入门教程(持续更新+中文字幕+普通话+不敷衍+义务教育+案例+学习)</a></p></blockquote><span id="more"></span><h2 id="案例"><a href="#案例" class="headerlink" title="案例"></a>案例</h2><h3 id="氮化镓充电器建模"><a href="#氮化镓充电器建模" class="headerlink" title="氮化镓充电器建模"></a>氮化镓充电器建模</h3><img src="/2024/02/28/Blender-%E9%BB%91%E4%BD%93%E9%AA%91%E5%A3%AB%E2%85%A1/%E6%B0%AE%E5%8C%96%E9%95%93%E5%85%85%E7%94%B5%E5%99%A8%E5%BB%BA%E6%A8%A1.png" class="" title="氮化镓充电器建模"><h3 id="氮化镓充电器PRO"><a href="#氮化镓充电器PRO" class="headerlink" title="氮化镓充电器PRO"></a>氮化镓充电器PRO</h3><img src="/2024/02/28/Blender-%E9%BB%91%E4%BD%93%E9%AA%91%E5%A3%AB%E2%85%A1/%E6%B0%AE%E5%8C%96%E9%95%93%E5%85%85%E7%94%B5%E5%99%A8PRO.png" class="" title="氮化镓充电器PRO">]]></content>
    
    
    <summary type="html">&lt;p&gt;Blender 练习案例&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;a href=&quot;https://www.bilibili.com/video/BV1zh411Y7LX?p=29&amp;spm_id_from=pageDriver&amp;vd_source=1e15750346a6b797fe55bee7e478af4c&quot;&gt;【合集8.21已更新93话】Blender 2.9-3.4黑铁骑士Ⅱ系统零基础入门教程(持续更新+中文字幕+普通话+不敷衍+义务教育+案例+学习)&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;</summary>
    
    
    
    <category term="教程学习" scheme="https://guangze233.com/categories/%E6%95%99%E7%A8%8B%E5%AD%A6%E4%B9%A0/"/>
    
    
    <category term="Blender" scheme="https://guangze233.com/tags/Blender/"/>
    
    <category term="3D建模" scheme="https://guangze233.com/tags/3D%E5%BB%BA%E6%A8%A1/"/>
    
    <category term="渲染" scheme="https://guangze233.com/tags/%E6%B8%B2%E6%9F%93/"/>
    
  </entry>
  
  <entry>
    <title>枘凿六合，天下第一</title>
    <link href="https://guangze233.com/2024/01/08/%E6%9E%98%E5%87%BF%E5%85%AD%E5%90%88%EF%BC%8C%E5%A4%A9%E4%B8%8B%E7%AC%AC%E4%B8%80/"/>
    <id>https://guangze233.com/2024/01/08/%E6%9E%98%E5%87%BF%E5%85%AD%E5%90%88%EF%BC%8C%E5%A4%A9%E4%B8%8B%E7%AC%AC%E4%B8%80/</id>
    <published>2024-01-08T06:29:04.000Z</published>
    <updated>2026-08-01T15:43:33.427Z</updated>
    
    <content type="html"><![CDATA[<p>【枘凿六合】是《崩坏：星穹铁道》中的一款二阶魔方解密游戏，记录 Threejs 实现过程。</p><span id="more"></span><img src="/2024/01/08/%E6%9E%98%E5%87%BF%E5%85%AD%E5%90%88%EF%BC%8C%E5%A4%A9%E4%B8%8B%E7%AC%AC%E4%B8%80/rzlh.gif" class="" title="枘凿六合，天下第一"><h1 id="背景"><a href="#背景" class="headerlink" title="背景"></a>背景</h1><p>启发来自 Threejs 的文章–<a href="https://threejs.org/manual/#zh/transparency">如何绘制透明的物体</a>，在网上搜了一圈也没有找到【枘凿六合】的例子，便想着自己实现一下。</p><p>不过魔方类的技术文章已经有很多实现了，如：</p><ul><li><a href="https://github.com/pancerZH/I-cannot-deal-with-Rubiks-cube">Github · I-cannot-deal-with-Rubiks-cube</a>: 使用 Three.js+Vue 编写的 3D 前端魔方游戏</li><li><a href="https://github.com/buuing/Rubiks-Cube">Github · Rubiks-Cube</a>: Three.js 实现魔方小游戏</li><li><a href="https://zhuanlan.zhihu.com/p/611744001?utm_id=0">知乎 · THREEJS 的三阶魔方</a></li><li><a href="https://zhuanlan.zhihu.com/p/33580374">知乎 · ThreeJS 四步制作一个简易魔方</a>：<strong>魔方旋转</strong></li><li><a href="https://www.mrguo.link/article?id=23">three.js 制作魔方</a>：了解<strong>tween 的补间动画</strong></li></ul><h1 id="简化"><a href="#简化" class="headerlink" title="简化"></a>简化</h1><h2 id="设想"><a href="#设想" class="headerlink" title="设想"></a>设想</h2><ol><li><a href="https://www.bilibili.com/video/BV1zh411Y7LX/?p=2&spm_id_from=pageDriver&vd_source=1e15750346a6b797fe55bee7e478af4c">【合集 8.21 已更新 93 话】Blender 2.9-3.4 黑铁骑士 Ⅱ 系统零基础入门教程(持续更新+中文字幕+普通话+不敷衍+义务教育+案例+学习)</a></li><li><a href="https://juejin.cn/post/7314850529111228451">three.js 辉光的两种实现方法，及解决添加辉光后背景变成黑色的问题</a></li></ol><h2 id="思路"><a href="#思路" class="headerlink" title="思路"></a>思路</h2><p>首先，【枘凿六合】是个二阶魔方，八个方块位于空间直角坐标系的八个卦限中，投射面板位置固定且坐标系不会转动，绘制应该不成问题。</p><p>然后是玩家操作上，仅可选择某一个面的方块，然后“左右”转动选择的方块。</p><p>当同一方向上的方块和投射面满足消消乐条件时，即可得分。</p><h1 id="实现"><a href="#实现" class="headerlink" title="实现"></a>实现</h1><h2 id="基础场景"><a href="#基础场景" class="headerlink" title="基础场景"></a>基础场景</h2><p>见文档：<a href="https://threejs.org/docs/index.html#manual/zh/introduction/Creating-a-scene">创建一个场景（Creating a scene）</a></p><h2 id="绘制方块"><a href="#绘制方块" class="headerlink" title="绘制方块"></a>绘制方块</h2><p>见文档：<a href="https://threejs.org/manual/#zh/transparency">如何绘制透明的物体</a></p><p>可按照卦限给方块编号 1~8，以配置文件的方式设置方块的颜色及附加信息，便于调整方块的颜色，构造不同关卡。<br>可附着一个 Box3 对象来标记选中时线框高亮。</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 方块分类</span></span><br><span class="line"><span class="keyword">const</span> <span class="title class_">ECubeType</span> = <span class="title class_">Object</span>.<span class="title function_">freeze</span>(&#123;</span><br><span class="line">  <span class="attr">solid</span>: &#123;</span><br><span class="line">    <span class="attr">color</span>: <span class="keyword">new</span> <span class="variable constant_">THREE</span>.<span class="title class_">Color</span>().<span class="title function_">setHSL</span>(<span class="number">0</span> / <span class="number">8</span>, <span class="number">1</span>, <span class="number">0.5</span>),</span><br><span class="line">    <span class="attr">value</span>: <span class="number">1</span>,</span><br><span class="line">  &#125;,</span><br><span class="line">  <span class="attr">hyaline</span>: &#123;</span><br><span class="line">    <span class="attr">color</span>: <span class="keyword">new</span> <span class="variable constant_">THREE</span>.<span class="title class_">Color</span>().<span class="title function_">setHSL</span>(<span class="number">3</span> / <span class="number">8</span>, <span class="number">1</span>, <span class="number">0.8</span>),</span><br><span class="line">    <span class="attr">value</span>: <span class="number">0</span>,</span><br><span class="line">  &#125;,</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 方块配置</span></span><br><span class="line"><span class="keyword">const</span> <span class="title class_">CubeCfg</span> = &#123;</span><br><span class="line">  <span class="attr">d</span>: <span class="number">0.8</span>,</span><br><span class="line">  <span class="attr">size</span>: <span class="number">1.2</span>,</span><br><span class="line">  <span class="attr">options</span>: [</span><br><span class="line">    &#123;</span><br><span class="line">      <span class="attr">name</span>: <span class="string">&quot;octant-1&quot;</span>,</span><br><span class="line">      <span class="attr">x</span>: <span class="number">1</span>,</span><br><span class="line">      <span class="attr">y</span>: <span class="number">1</span>,</span><br><span class="line">      <span class="attr">z</span>: <span class="number">1</span>,</span><br><span class="line">      <span class="attr">type</span>: <span class="string">&quot;hyaline&quot;</span>,</span><br><span class="line">    &#125;,...</span><br><span class="line">  ],</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 创建方块</span></span><br><span class="line"><span class="variable language_">this</span>.<span class="property">cubeGroup</span> = <span class="variable language_">this</span>.<span class="title function_">_createCubes</span>(<span class="title class_">CubeCfg</span>);</span><br><span class="line"><span class="variable language_">this</span>.<span class="property">scene</span>.<span class="title function_">add</span>(<span class="variable language_">this</span>.<span class="property">cubeGroup</span>);</span><br></pre></td></tr></table></figure><h2 id="选择共面"><a href="#选择共面" class="headerlink" title="选择共面"></a>选择共面</h2><p>如何选择共面呢？</p><p>在崩铁中通过点击方向箭头选择共面，并添加选中效果（发光）；这里直接搞六个选择共面的按钮和左右按钮，用相对定位糊上即可。</p><p>如何确定选中哪些方块呢，毕竟方块是可以旋转走的？</p><ul><li><a href="https://threejs.org/docs/index.html#api/zh/helpers/Box3Helper">Box3Helper</a>：设置六个 Box3Helper 并编号，每个 Box3Helper 都能包含四个位置；使用其 Box 对象的 <a href="https://threejs.org/docs/index.html#api/zh/math/Box3.distanceToPoint">distanceToPoint</a> 方法来判断方块是否在选中的 Box3Helper 范围内，即方块是否选中。</li><li><a href="https://threejs.org/docs/?q=R#api/zh/core/Raycaster">Raycaster</a>：设置八条射线，可组合出六个面，通过射线相交来找选中的方块；且射线也可监测出投射面板，检查结果也更方便。</li></ul><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment">  * 创建射线组</span></span><br><span class="line"><span class="comment">  */</span></span><br><span class="line"><span class="title function_">_createRaycasters</span>(<span class="params">d, size</span>) &#123;</span><br><span class="line">    <span class="keyword">const</span> raycasterOptions = [</span><br><span class="line">      <span class="comment">/**</span></span><br><span class="line"><span class="comment">       * 投射面: XY平面</span></span><br><span class="line"><span class="comment">       * 方向：Z负半轴</span></span><br><span class="line"><span class="comment">       */</span></span><br><span class="line">      &#123;</span><br><span class="line">        <span class="attr">name</span>: <span class="string">&quot;raycaster-1&quot;</span>,</span><br><span class="line">        <span class="attr">origin</span>: <span class="keyword">new</span> <span class="variable constant_">THREE</span>.<span class="title class_">Vector3</span>(d, d, d + size),</span><br><span class="line">        <span class="attr">direction</span>: <span class="keyword">new</span> <span class="variable constant_">THREE</span>.<span class="title class_">Vector3</span>(<span class="number">0</span>, <span class="number">0</span>, -<span class="number">1</span>),</span><br><span class="line">        <span class="attr">type</span>: <span class="string">&quot;solid&quot;</span>,</span><br><span class="line">      &#125;,...</span><br><span class="line">   ]</span><br><span class="line">   <span class="keyword">return</span> raycasterOptions</span><br></pre></td></tr></table></figure><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 创建投射组</span></span><br><span class="line"><span class="variable language_">this</span>.<span class="property">raycasterOptions</span> = <span class="variable language_">this</span>.<span class="title function_">_createRaycasters</span>(<span class="title class_">CubeCfg</span>.<span class="property">d</span>, <span class="title class_">CubeCfg</span>.<span class="property">size</span>);</span><br><span class="line"><span class="comment">// 投射板</span></span><br><span class="line"><span class="variable language_">this</span>.<span class="property">boardGroup</span> = <span class="variable language_">this</span>.<span class="title function_">_addProjectionBoard</span>(<span class="variable language_">this</span>.<span class="property">raycasterOptions</span>, <span class="title class_">CubeCfg</span>.<span class="property">d</span>, <span class="title class_">CubeCfg</span>.<span class="property">size</span>);</span><br><span class="line"><span class="variable language_">this</span>.<span class="property">scene</span>.<span class="title function_">add</span>(<span class="variable language_">this</span>.<span class="property">boardGroup</span>);</span><br><span class="line"><span class="comment">// 划分选择面</span></span><br><span class="line"><span class="variable language_">this</span>.<span class="property">planeOptions</span> = [</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="attr">name</span>: <span class="string">&quot;plane-1&quot;</span>,</span><br><span class="line">        <span class="attr">raycasterIndexs</span>: [<span class="number">0</span>, <span class="number">1</span>], <span class="comment">// xz,y&gt;0</span></span><br><span class="line">        <span class="attr">rotateAxis</span>: <span class="keyword">new</span> <span class="variable constant_">THREE</span>.<span class="title class_">Vector3</span>(<span class="number">0</span>, <span class="number">1</span>, <span class="number">0</span>),</span><br><span class="line">        <span class="attr">leftRad</span>: <span class="title class_">Math</span>.<span class="property">PI</span> / <span class="number">2</span>,</span><br><span class="line">        <span class="attr">rightRad</span>: -<span class="title class_">Math</span>.<span class="property">PI</span> / <span class="number">2</span>,</span><br><span class="line">    &#125;,...</span><br><span class="line">]</span><br></pre></td></tr></table></figure><h2 id="旋转方块"><a href="#旋转方块" class="headerlink" title="旋转方块"></a>旋转方块</h2><p>一开始调用 Object3D 的一些旋转 API（rotateOnAxis、rotateOnWorldAxis、rotateX…），结果是方块原地打转（方块各面纹理不同时可观察出来）。</p><p>是 Object3D 本地坐标系的原因，后来参考<a href="https://zhuanlan.zhihu.com/p/33580374">知乎 · ThreeJS 四步制作一个简易魔方</a>评论区方法，秒了。</p><img src="/2024/01/08/%E6%9E%98%E5%87%BF%E5%85%AD%E5%90%88%EF%BC%8C%E5%A4%A9%E4%B8%8B%E7%AC%AC%E4%B8%80/rotation.webp" class="" title="绕轴旋转"><p>注意：这里的旋转仅仅是方块位置（看成中心点）的旋转，实际上方块旋转过程中还应该考虑自身的旋转，否则动画效果会比较怪异。</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment">   * <span class="doctag">@descption</span> 旋转动画</span></span><br><span class="line"><span class="comment">   * <span class="doctag">@param</span> &#123;<span class="type">*</span>&#125; cubes 旋转的方块</span></span><br><span class="line"><span class="comment">   * <span class="doctag">@param</span> &#123;<span class="type">*</span>&#125; rotateAxis 旋转轴</span></span><br><span class="line"><span class="comment">   * <span class="doctag">@param</span> &#123;<span class="type">*</span>&#125; rad 旋转角度</span></span><br><span class="line"><span class="comment">   */</span></span><br><span class="line">  <span class="title function_">_rotate</span>(<span class="params">cubes, rotateAxis, rad</span>) &#123;</span><br><span class="line">    <span class="comment">// 更新起始数据</span></span><br><span class="line">    cubes.<span class="title function_">forEach</span>(<span class="function">(<span class="params">cube</span>) =&gt;</span> &#123;</span><br><span class="line">      cube.<span class="property">recoardStart</span> = &#123;</span><br><span class="line">        <span class="attr">position</span>: cube.<span class="property">position</span>.<span class="title function_">clone</span>(),</span><br><span class="line">        <span class="attr">rotation</span>: <span class="keyword">new</span> <span class="variable constant_">THREE</span>.<span class="title class_">Vector3</span>(<span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>),</span><br><span class="line">      &#125;;</span><br><span class="line">      cube.<span class="property">rotation</span>.<span class="property">x</span> = <span class="number">0</span>;</span><br><span class="line">      cube.<span class="property">rotation</span>.<span class="property">y</span> = <span class="number">0</span>;</span><br><span class="line">      cube.<span class="property">rotation</span>.<span class="property">z</span> = <span class="number">0</span>;</span><br><span class="line">    &#125;);</span><br><span class="line">    <span class="comment">// 旋转动画</span></span><br><span class="line">    <span class="keyword">const</span> <span class="variable constant_">LOCAL_AXIS</span> = rotateAxis.<span class="property">x</span> ? <span class="string">&quot;x&quot;</span> : rotateAxis.<span class="property">y</span> ? <span class="string">&quot;y&quot;</span> : <span class="string">&quot;z&quot;</span>;</span><br><span class="line">    <span class="keyword">const</span> <span class="variable constant_">TWEEN_DURATION</span> = <span class="number">1000</span>;</span><br><span class="line">    <span class="keyword">const</span> tween = <span class="keyword">new</span> <span class="variable constant_">TWEEN</span>.<span class="title class_">Tween</span>(&#123; <span class="attr">percentage</span>: <span class="number">0</span> &#125;);</span><br><span class="line">    tween.<span class="title function_">easing</span>(<span class="variable constant_">TWEEN</span>.<span class="property">Easing</span>.<span class="property">Quartic</span>.<span class="property">InOut</span>);</span><br><span class="line">    tween.<span class="title function_">onUpdate</span>(<span class="function">(<span class="params">&#123; percentage &#125;</span>) =&gt;</span> &#123;</span><br><span class="line">      cubes.<span class="title function_">forEach</span>(<span class="function">(<span class="params">cube</span>) =&gt;</span> &#123;</span><br><span class="line">        <span class="comment">// 旋转面</span></span><br><span class="line">        cube.<span class="property">rotation</span>[<span class="variable constant_">LOCAL_AXIS</span>] =</span><br><span class="line">          cube.<span class="property">recoardStart</span>.<span class="property">rotation</span>[<span class="variable constant_">LOCAL_AXIS</span>] + percentage * rad;</span><br><span class="line">        <span class="keyword">const</span> newPosition = cube.<span class="property">recoardStart</span>.<span class="property">position</span>.<span class="title function_">clone</span>();</span><br><span class="line">        newPosition.<span class="title function_">applyAxisAngle</span>(rotateAxis, percentage * rad);</span><br><span class="line">        <span class="comment">// 旋转位置</span></span><br><span class="line">        cube.<span class="property">position</span>.<span class="title function_">set</span>(newPosition.<span class="property">x</span>, newPosition.<span class="property">y</span>, newPosition.<span class="property">z</span>);</span><br><span class="line">      &#125;);</span><br><span class="line">    &#125;);</span><br><span class="line">    tween.<span class="title function_">onComplete</span>(<span class="function">() =&gt;</span> &#123;</span><br><span class="line">      <span class="comment">/** 校验结果 */</span></span><br><span class="line">      <span class="variable language_">this</span>.<span class="title function_">_check</span>();</span><br><span class="line">    &#125;);</span><br><span class="line">    tween.<span class="title function_">to</span>(&#123; <span class="attr">percentage</span>: <span class="number">1</span> &#125;, <span class="variable constant_">TWEEN_DURATION</span>);</span><br><span class="line">    tween.<span class="title function_">start</span>();</span><br><span class="line">  &#125;</span><br></pre></td></tr></table></figure><h2 id="胜利结算"><a href="#胜利结算" class="headerlink" title="胜利结算"></a>胜利结算</h2><p>校验结果比较简单：</p><ol><li>每条射线穿过 2 个方块和一个投射面，若满足消消乐规则，则给投射面一个高亮效果，代表成功；</li><li>若所有投射面都高亮，则表示关卡胜利。</li></ol><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment">   * 校验结果</span></span><br><span class="line"><span class="comment">   */</span></span><br><span class="line">  <span class="title function_">_check</span>(<span class="params"></span>) &#123;</span><br><span class="line">    <span class="variable language_">this</span>.<span class="property">raycasterOptions</span>.<span class="title function_">forEach</span>(<span class="function">(<span class="params">e</span>) =&gt;</span> &#123;</span><br><span class="line">      <span class="keyword">const</span> checkSet = <span class="keyword">new</span> <span class="title class_">Set</span>();</span><br><span class="line">      <span class="variable language_">this</span>.<span class="property">gzsRaycaster</span>.<span class="title function_">set</span>(e.<span class="property">origin</span>, e.<span class="property">direction</span>);</span><br><span class="line">      <span class="keyword">const</span> intersects = <span class="variable language_">this</span>.<span class="property">gzsRaycaster</span>.<span class="title function_">intersectObjects</span>(</span><br><span class="line">        <span class="variable language_">this</span>.<span class="property">scene</span>.<span class="property">children</span></span><br><span class="line">      );</span><br><span class="line">      <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i &lt; intersects.<span class="property">length</span>; i++) &#123;</span><br><span class="line">        <span class="keyword">if</span> (intersects[i].<span class="property">object</span>.<span class="property">isMesh</span>) &#123;</span><br><span class="line">          checkSet.<span class="title function_">add</span>(intersects[i].<span class="property">object</span>);</span><br><span class="line">        &#125;</span><br><span class="line">      &#125;</span><br><span class="line">      <span class="keyword">const</span> checkArr = <span class="title class_">Array</span>.<span class="title function_">from</span>(checkSet);</span><br><span class="line">      checkArr[<span class="number">2</span>].<span class="title function_">trigger</span>(</span><br><span class="line">        checkArr[<span class="number">0</span>].<span class="property">rccs</span>.<span class="property">value</span> === checkArr[<span class="number">1</span>].<span class="property">rccs</span>.<span class="property">value</span> &amp;&amp;</span><br><span class="line">          checkArr[<span class="number">1</span>].<span class="property">rccs</span>.<span class="property">value</span> === checkArr[<span class="number">2</span>].<span class="property">rccs</span>.<span class="property">value</span></span><br><span class="line">      );</span><br><span class="line">    &#125;);</span><br><span class="line">    <span class="keyword">let</span> pass = <span class="variable language_">this</span>.<span class="property">boardGroup</span>.<span class="property">children</span>.<span class="title function_">every</span>(<span class="function">(<span class="params">e</span>) =&gt;</span> e.<span class="property">children</span>[<span class="number">0</span>].<span class="property">visible</span>);</span><br><span class="line">    <span class="built_in">setTimeout</span>(<span class="function">() =&gt;</span> &#123;</span><br><span class="line">      <span class="keyword">if</span> (pass) &#123;</span><br><span class="line">        <span class="title function_">alert</span>(<span class="string">&quot;出院！&quot;</span>);</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;, <span class="number">100</span>);</span><br><span class="line">  &#125;</span><br></pre></td></tr></table></figure><h1 id="优化"><a href="#优化" class="headerlink" title="优化"></a>优化</h1><p>UI太丑，魔方可用贴图或模型。下一步尝试 blender 建模。</p><h1 id="代码"><a href="#代码" class="headerlink" title="代码"></a>代码</h1><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br><span class="line">216</span><br><span class="line">217</span><br><span class="line">218</span><br><span class="line">219</span><br><span class="line">220</span><br><span class="line">221</span><br><span class="line">222</span><br><span class="line">223</span><br><span class="line">224</span><br><span class="line">225</span><br><span class="line">226</span><br><span class="line">227</span><br><span class="line">228</span><br><span class="line">229</span><br><span class="line">230</span><br><span class="line">231</span><br><span class="line">232</span><br><span class="line">233</span><br><span class="line">234</span><br><span class="line">235</span><br><span class="line">236</span><br><span class="line">237</span><br><span class="line">238</span><br><span class="line">239</span><br><span class="line">240</span><br><span class="line">241</span><br><span class="line">242</span><br><span class="line">243</span><br><span class="line">244</span><br><span class="line">245</span><br><span class="line">246</span><br><span class="line">247</span><br><span class="line">248</span><br><span class="line">249</span><br><span class="line">250</span><br><span class="line">251</span><br><span class="line">252</span><br><span class="line">253</span><br><span class="line">254</span><br><span class="line">255</span><br><span class="line">256</span><br><span class="line">257</span><br><span class="line">258</span><br><span class="line">259</span><br><span class="line">260</span><br><span class="line">261</span><br><span class="line">262</span><br><span class="line">263</span><br><span class="line">264</span><br><span class="line">265</span><br><span class="line">266</span><br><span class="line">267</span><br><span class="line">268</span><br><span class="line">269</span><br><span class="line">270</span><br><span class="line">271</span><br><span class="line">272</span><br><span class="line">273</span><br><span class="line">274</span><br><span class="line">275</span><br><span class="line">276</span><br><span class="line">277</span><br><span class="line">278</span><br><span class="line">279</span><br><span class="line">280</span><br><span class="line">281</span><br><span class="line">282</span><br><span class="line">283</span><br><span class="line">284</span><br><span class="line">285</span><br><span class="line">286</span><br><span class="line">287</span><br><span class="line">288</span><br><span class="line">289</span><br><span class="line">290</span><br><span class="line">291</span><br><span class="line">292</span><br><span class="line">293</span><br><span class="line">294</span><br><span class="line">295</span><br><span class="line">296</span><br><span class="line">297</span><br><span class="line">298</span><br><span class="line">299</span><br><span class="line">300</span><br><span class="line">301</span><br><span class="line">302</span><br><span class="line">303</span><br><span class="line">304</span><br><span class="line">305</span><br><span class="line">306</span><br><span class="line">307</span><br><span class="line">308</span><br><span class="line">309</span><br><span class="line">310</span><br><span class="line">311</span><br><span class="line">312</span><br><span class="line">313</span><br><span class="line">314</span><br><span class="line">315</span><br><span class="line">316</span><br><span class="line">317</span><br><span class="line">318</span><br><span class="line">319</span><br><span class="line">320</span><br><span class="line">321</span><br><span class="line">322</span><br><span class="line">323</span><br><span class="line">324</span><br><span class="line">325</span><br><span class="line">326</span><br><span class="line">327</span><br><span class="line">328</span><br><span class="line">329</span><br><span class="line">330</span><br><span class="line">331</span><br><span class="line">332</span><br><span class="line">333</span><br><span class="line">334</span><br><span class="line">335</span><br><span class="line">336</span><br><span class="line">337</span><br><span class="line">338</span><br><span class="line">339</span><br><span class="line">340</span><br><span class="line">341</span><br><span class="line">342</span><br><span class="line">343</span><br><span class="line">344</span><br><span class="line">345</span><br><span class="line">346</span><br><span class="line">347</span><br><span class="line">348</span><br><span class="line">349</span><br><span class="line">350</span><br><span class="line">351</span><br><span class="line">352</span><br><span class="line">353</span><br><span class="line">354</span><br><span class="line">355</span><br><span class="line">356</span><br><span class="line">357</span><br><span class="line">358</span><br><span class="line">359</span><br><span class="line">360</span><br><span class="line">361</span><br><span class="line">362</span><br><span class="line">363</span><br><span class="line">364</span><br><span class="line">365</span><br><span class="line">366</span><br><span class="line">367</span><br><span class="line">368</span><br><span class="line">369</span><br><span class="line">370</span><br><span class="line">371</span><br><span class="line">372</span><br><span class="line">373</span><br><span class="line">374</span><br><span class="line">375</span><br><span class="line">376</span><br><span class="line">377</span><br><span class="line">378</span><br><span class="line">379</span><br><span class="line">380</span><br><span class="line">381</span><br><span class="line">382</span><br><span class="line">383</span><br><span class="line">384</span><br><span class="line">385</span><br><span class="line">386</span><br><span class="line">387</span><br><span class="line">388</span><br><span class="line">389</span><br><span class="line">390</span><br><span class="line">391</span><br><span class="line">392</span><br><span class="line">393</span><br><span class="line">394</span><br><span class="line">395</span><br><span class="line">396</span><br><span class="line">397</span><br><span class="line">398</span><br><span class="line">399</span><br><span class="line">400</span><br><span class="line">401</span><br><span class="line">402</span><br><span class="line">403</span><br><span class="line">404</span><br><span class="line">405</span><br><span class="line">406</span><br><span class="line">407</span><br><span class="line">408</span><br><span class="line">409</span><br><span class="line">410</span><br><span class="line">411</span><br><span class="line">412</span><br><span class="line">413</span><br><span class="line">414</span><br><span class="line">415</span><br><span class="line">416</span><br><span class="line">417</span><br><span class="line">418</span><br><span class="line">419</span><br><span class="line">420</span><br><span class="line">421</span><br><span class="line">422</span><br><span class="line">423</span><br><span class="line">424</span><br><span class="line">425</span><br><span class="line">426</span><br><span class="line">427</span><br><span class="line">428</span><br><span class="line">429</span><br><span class="line">430</span><br><span class="line">431</span><br><span class="line">432</span><br><span class="line">433</span><br><span class="line">434</span><br><span class="line">435</span><br><span class="line">436</span><br><span class="line">437</span><br><span class="line">438</span><br><span class="line">439</span><br><span class="line">440</span><br><span class="line">441</span><br><span class="line">442</span><br><span class="line">443</span><br><span class="line">444</span><br><span class="line">445</span><br><span class="line">446</span><br><span class="line">447</span><br><span class="line">448</span><br><span class="line">449</span><br><span class="line">450</span><br><span class="line">451</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 枘凿六合</span></span><br><span class="line"><span class="keyword">import</span> * <span class="keyword">as</span> <span class="variable constant_">THREE</span> <span class="keyword">from</span> <span class="string">&quot;three&quot;</span>;</span><br><span class="line"><span class="keyword">import</span> * <span class="keyword">as</span> <span class="variable constant_">TWEEN</span> <span class="keyword">from</span> <span class="string">&quot;@tweenjs/tween.js&quot;</span>;</span><br><span class="line"><span class="keyword">import</span> <span class="title class_">ThreeMap</span> <span class="keyword">from</span> <span class="string">&quot;./ThreeMap&quot;</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> <span class="title class_">ECubeType</span> = <span class="title class_">Object</span>.<span class="title function_">freeze</span>(&#123;</span><br><span class="line">  <span class="attr">solid</span>: &#123;</span><br><span class="line">    <span class="attr">color</span>: <span class="keyword">new</span> <span class="variable constant_">THREE</span>.<span class="title class_">Color</span>().<span class="title function_">setHSL</span>(<span class="number">0</span> / <span class="number">8</span>, <span class="number">1</span>, <span class="number">0.5</span>),</span><br><span class="line">    <span class="attr">value</span>: <span class="number">1</span>,</span><br><span class="line">  &#125;,</span><br><span class="line">  <span class="attr">hyaline</span>: &#123;</span><br><span class="line">    <span class="attr">color</span>: <span class="keyword">new</span> <span class="variable constant_">THREE</span>.<span class="title class_">Color</span>().<span class="title function_">setHSL</span>(<span class="number">3</span> / <span class="number">8</span>, <span class="number">1</span>, <span class="number">0.8</span>),</span><br><span class="line">    <span class="attr">value</span>: <span class="number">0</span>,</span><br><span class="line">  &#125;,</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> <span class="title class_">CubeCfg</span> = &#123;</span><br><span class="line">  <span class="attr">d</span>: <span class="number">0.8</span>,</span><br><span class="line">  <span class="attr">size</span>: <span class="number">1.2</span>,</span><br><span class="line">  <span class="attr">options</span>: [</span><br><span class="line">    &#123;</span><br><span class="line">      <span class="attr">name</span>: <span class="string">&quot;octant-1&quot;</span>,</span><br><span class="line">      <span class="attr">x</span>: <span class="number">1</span>,</span><br><span class="line">      <span class="attr">y</span>: <span class="number">1</span>,</span><br><span class="line">      <span class="attr">z</span>: <span class="number">1</span>,</span><br><span class="line">      <span class="attr">type</span>: <span class="string">&quot;hyaline&quot;</span>,</span><br><span class="line">    &#125;,</span><br><span class="line">    &#123;</span><br><span class="line">      <span class="attr">name</span>: <span class="string">&quot;octant-2&quot;</span>,</span><br><span class="line">      <span class="attr">x</span>: -<span class="number">1</span>,</span><br><span class="line">      <span class="attr">y</span>: <span class="number">1</span>,</span><br><span class="line">      <span class="attr">z</span>: <span class="number">1</span>,</span><br><span class="line">      <span class="attr">type</span>: <span class="string">&quot;solid&quot;</span>,</span><br><span class="line">    &#125;,</span><br><span class="line">    &#123;</span><br><span class="line">      <span class="attr">name</span>: <span class="string">&quot;octant-3&quot;</span>,</span><br><span class="line">      <span class="attr">x</span>: -<span class="number">1</span>,</span><br><span class="line">      <span class="attr">y</span>: -<span class="number">1</span>,</span><br><span class="line">      <span class="attr">z</span>: <span class="number">1</span>,</span><br><span class="line">      <span class="attr">type</span>: <span class="string">&quot;solid&quot;</span>,</span><br><span class="line">    &#125;,</span><br><span class="line">    &#123;</span><br><span class="line">      <span class="attr">name</span>: <span class="string">&quot;octant-4&quot;</span>,</span><br><span class="line">      <span class="attr">x</span>: <span class="number">1</span>,</span><br><span class="line">      <span class="attr">y</span>: -<span class="number">1</span>,</span><br><span class="line">      <span class="attr">z</span>: <span class="number">1</span>,</span><br><span class="line">      <span class="attr">type</span>: <span class="string">&quot;hyaline&quot;</span>,</span><br><span class="line">    &#125;,</span><br><span class="line">    &#123;</span><br><span class="line">      <span class="attr">name</span>: <span class="string">&quot;octant-5&quot;</span>,</span><br><span class="line">      <span class="attr">x</span>: <span class="number">1</span>,</span><br><span class="line">      <span class="attr">y</span>: <span class="number">1</span>,</span><br><span class="line">      <span class="attr">z</span>: -<span class="number">1</span>,</span><br><span class="line">      <span class="attr">type</span>: <span class="string">&quot;solid&quot;</span>,</span><br><span class="line">    &#125;,</span><br><span class="line">    &#123;</span><br><span class="line">      <span class="attr">name</span>: <span class="string">&quot;octant-6&quot;</span>,</span><br><span class="line">      <span class="attr">x</span>: -<span class="number">1</span>,</span><br><span class="line">      <span class="attr">y</span>: <span class="number">1</span>,</span><br><span class="line">      <span class="attr">z</span>: -<span class="number">1</span>,</span><br><span class="line">      <span class="attr">type</span>: <span class="string">&quot;hyaline&quot;</span>,</span><br><span class="line">    &#125;,</span><br><span class="line">    &#123;</span><br><span class="line">      <span class="attr">name</span>: <span class="string">&quot;octant-7&quot;</span>,</span><br><span class="line">      <span class="attr">x</span>: -<span class="number">1</span>,</span><br><span class="line">      <span class="attr">y</span>: -<span class="number">1</span>,</span><br><span class="line">      <span class="attr">z</span>: -<span class="number">1</span>,</span><br><span class="line">      <span class="attr">type</span>: <span class="string">&quot;hyaline&quot;</span>,</span><br><span class="line">    &#125;,</span><br><span class="line">    &#123;</span><br><span class="line">      <span class="attr">name</span>: <span class="string">&quot;octant-8&quot;</span>,</span><br><span class="line">      <span class="attr">x</span>: <span class="number">1</span>,</span><br><span class="line">      <span class="attr">y</span>: -<span class="number">1</span>,</span><br><span class="line">      <span class="attr">z</span>: -<span class="number">1</span>,</span><br><span class="line">      <span class="attr">type</span>: <span class="string">&quot;solid&quot;</span>,</span><br><span class="line">    &#125;,</span><br><span class="line">  ],</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="keyword">class</span> <span class="title class_">GzsThreeMap</span> <span class="keyword">extends</span> <span class="title class_ inherited__">ThreeMap</span> &#123;</span><br><span class="line">  <span class="title function_">_initView</span>(<span class="params"></span>) &#123;</span><br><span class="line">    <span class="variable language_">this</span>.<span class="title function_">_createScene</span>();</span><br><span class="line">    <span class="variable language_">this</span>.<span class="title function_">_addLight</span>(-<span class="number">1</span>, <span class="number">2</span>, <span class="number">4</span>);</span><br><span class="line">    <span class="variable language_">this</span>.<span class="title function_">_addLight</span>(<span class="number">1</span>, -<span class="number">1</span>, -<span class="number">2</span>);</span><br><span class="line">    <span class="variable language_">this</span>.<span class="title function_">_createCamera</span>(<span class="variable language_">this</span>.<span class="property">options</span>.<span class="property">camera</span>.<span class="property">far</span>);</span><br><span class="line">    <span class="variable language_">this</span>.<span class="title function_">_createRender</span>(<span class="variable language_">this</span>.<span class="property">rootElement</span>);</span><br><span class="line">    <span class="variable language_">this</span>.<span class="title function_">_createControls</span>(<span class="variable language_">this</span>.<span class="property">camera</span>, <span class="variable language_">this</span>.<span class="property">renderer</span>.<span class="property">domElement</span>);</span><br><span class="line">    <span class="variable language_">this</span>.<span class="title function_">_createAxesHelper</span>();</span><br><span class="line">    <span class="variable language_">this</span>.<span class="title function_">switchAnimate</span>();</span><br><span class="line">    <span class="comment">// 相机归位</span></span><br><span class="line">    <span class="variable language_">this</span>.<span class="property">camera</span>.<span class="property">position</span>.<span class="title function_">set</span>(<span class="number">4.6</span>, <span class="number">3</span>, <span class="number">5.14</span>);</span><br><span class="line">    <span class="variable language_">this</span>.<span class="property">camera</span>.<span class="property">up</span>.<span class="title function_">set</span>(<span class="number">0</span>, -<span class="number">1</span>, <span class="number">0</span>);</span><br><span class="line">    <span class="variable language_">this</span>.<span class="property">camera</span>.<span class="title function_">lookAt</span>(<span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>);</span><br><span class="line">    <span class="comment">// 创建方块</span></span><br><span class="line">    <span class="variable language_">this</span>.<span class="property">cubeGroup</span> = <span class="variable language_">this</span>.<span class="title function_">_createCubes</span>(<span class="title class_">CubeCfg</span>);</span><br><span class="line">    <span class="variable language_">this</span>.<span class="property">scene</span>.<span class="title function_">add</span>(<span class="variable language_">this</span>.<span class="property">cubeGroup</span>);</span><br><span class="line">    <span class="comment">// 创建投射组</span></span><br><span class="line">    <span class="variable language_">this</span>.<span class="property">raycasterOptions</span> = <span class="variable language_">this</span>.<span class="title function_">_createRaycasters</span>(<span class="title class_">CubeCfg</span>.<span class="property">d</span>, <span class="title class_">CubeCfg</span>.<span class="property">size</span>);</span><br><span class="line">    <span class="comment">// 投射板</span></span><br><span class="line">    <span class="variable language_">this</span>.<span class="property">boardGroup</span> = <span class="variable language_">this</span>.<span class="title function_">_addProjectionBoard</span>(</span><br><span class="line">      <span class="variable language_">this</span>.<span class="property">raycasterOptions</span>,</span><br><span class="line">      <span class="title class_">CubeCfg</span>.<span class="property">d</span>,</span><br><span class="line">      <span class="title class_">CubeCfg</span>.<span class="property">size</span></span><br><span class="line">    );</span><br><span class="line">    <span class="variable language_">this</span>.<span class="property">scene</span>.<span class="title function_">add</span>(<span class="variable language_">this</span>.<span class="property">boardGroup</span>);</span><br><span class="line">    <span class="comment">// 划分选择面</span></span><br><span class="line">    <span class="variable language_">this</span>.<span class="property">planeOptions</span> = [</span><br><span class="line">      &#123;</span><br><span class="line">        <span class="attr">name</span>: <span class="string">&quot;plane-1&quot;</span>,</span><br><span class="line">        <span class="attr">raycasterIndexs</span>: [<span class="number">0</span>, <span class="number">1</span>], <span class="comment">// xz,y&gt;0</span></span><br><span class="line">        <span class="attr">rotateAxis</span>: <span class="keyword">new</span> <span class="variable constant_">THREE</span>.<span class="title class_">Vector3</span>(<span class="number">0</span>, <span class="number">1</span>, <span class="number">0</span>),</span><br><span class="line">        <span class="attr">leftRad</span>: <span class="title class_">Math</span>.<span class="property">PI</span> / <span class="number">2</span>,</span><br><span class="line">        <span class="attr">rightRad</span>: -<span class="title class_">Math</span>.<span class="property">PI</span> / <span class="number">2</span>,</span><br><span class="line">      &#125;,</span><br><span class="line">      &#123;</span><br><span class="line">        <span class="attr">name</span>: <span class="string">&quot;plane-2&quot;</span>,</span><br><span class="line">        <span class="attr">raycasterIndexs</span>: [<span class="number">2</span>, <span class="number">3</span>], <span class="comment">// xz,y&lt;0</span></span><br><span class="line">        <span class="attr">rotateAxis</span>: <span class="keyword">new</span> <span class="variable constant_">THREE</span>.<span class="title class_">Vector3</span>(<span class="number">0</span>, <span class="number">1</span>, <span class="number">0</span>),</span><br><span class="line">        <span class="attr">leftRad</span>: <span class="title class_">Math</span>.<span class="property">PI</span> / <span class="number">2</span>,</span><br><span class="line">        <span class="attr">rightRad</span>: -<span class="title class_">Math</span>.<span class="property">PI</span> / <span class="number">2</span>,</span><br><span class="line">      &#125;,</span><br><span class="line">      &#123;</span><br><span class="line">        <span class="attr">name</span>: <span class="string">&quot;plane-3&quot;</span>,</span><br><span class="line">        <span class="attr">raycasterIndexs</span>: [<span class="number">0</span>, <span class="number">3</span>], <span class="comment">// yz,x&gt;0</span></span><br><span class="line">        <span class="attr">rotateAxis</span>: <span class="keyword">new</span> <span class="variable constant_">THREE</span>.<span class="title class_">Vector3</span>(<span class="number">1</span>, <span class="number">0</span>, <span class="number">0</span>),</span><br><span class="line">        <span class="attr">leftRad</span>: <span class="title class_">Math</span>.<span class="property">PI</span> / <span class="number">2</span>,</span><br><span class="line">        <span class="attr">rightRad</span>: -<span class="title class_">Math</span>.<span class="property">PI</span> / <span class="number">2</span>,</span><br><span class="line">      &#125;,</span><br><span class="line">      &#123;</span><br><span class="line">        <span class="attr">name</span>: <span class="string">&quot;plane-4&quot;</span>,</span><br><span class="line">        <span class="attr">raycasterIndexs</span>: [<span class="number">1</span>, <span class="number">2</span>], <span class="comment">// yz,x&lt;0</span></span><br><span class="line">        <span class="attr">rotateAxis</span>: <span class="keyword">new</span> <span class="variable constant_">THREE</span>.<span class="title class_">Vector3</span>(<span class="number">1</span>, <span class="number">0</span>, <span class="number">0</span>),</span><br><span class="line">        <span class="attr">leftRad</span>: <span class="title class_">Math</span>.<span class="property">PI</span> / <span class="number">2</span>,</span><br><span class="line">        <span class="attr">rightRad</span>: -<span class="title class_">Math</span>.<span class="property">PI</span> / <span class="number">2</span>,</span><br><span class="line">      &#125;,</span><br><span class="line">      &#123;</span><br><span class="line">        <span class="attr">name</span>: <span class="string">&quot;plane-5&quot;</span>,</span><br><span class="line">        <span class="attr">raycasterIndexs</span>: [<span class="number">4</span>, <span class="number">7</span>], <span class="comment">// xy,z&gt;0</span></span><br><span class="line">        <span class="attr">rotateAxis</span>: <span class="keyword">new</span> <span class="variable constant_">THREE</span>.<span class="title class_">Vector3</span>(<span class="number">0</span>, <span class="number">0</span>, <span class="number">1</span>),</span><br><span class="line">        <span class="attr">leftRad</span>: <span class="title class_">Math</span>.<span class="property">PI</span> / <span class="number">2</span>,</span><br><span class="line">        <span class="attr">rightRad</span>: -<span class="title class_">Math</span>.<span class="property">PI</span> / <span class="number">2</span>,</span><br><span class="line">      &#125;,</span><br><span class="line">      &#123;</span><br><span class="line">        <span class="attr">name</span>: <span class="string">&quot;plane-6&quot;</span>,</span><br><span class="line">        <span class="attr">raycasterIndexs</span>: [<span class="number">5</span>, <span class="number">6</span>], <span class="comment">// yz,x&lt;0</span></span><br><span class="line">        <span class="attr">rotateAxis</span>: <span class="keyword">new</span> <span class="variable constant_">THREE</span>.<span class="title class_">Vector3</span>(<span class="number">0</span>, <span class="number">0</span>, <span class="number">1</span>),</span><br><span class="line">        <span class="attr">leftRad</span>: <span class="title class_">Math</span>.<span class="property">PI</span> / <span class="number">2</span>,</span><br><span class="line">        <span class="attr">rightRad</span>: -<span class="title class_">Math</span>.<span class="property">PI</span> / <span class="number">2</span>,</span><br><span class="line">      &#125;,</span><br><span class="line">    ];</span><br><span class="line">    <span class="comment">// 投射</span></span><br><span class="line">    <span class="variable language_">this</span>.<span class="property">gzsRaycaster</span> = <span class="keyword">new</span> <span class="variable constant_">THREE</span>.<span class="title class_">Raycaster</span>();</span><br><span class="line">    <span class="variable language_">this</span>.<span class="property">gzsRaycaster</span>.<span class="property">camera</span> = <span class="variable language_">this</span>.<span class="property">camera</span>;</span><br><span class="line">    <span class="comment">// 当前选中面</span></span><br><span class="line">    <span class="variable language_">this</span>.<span class="property">currentPlane</span> = <span class="literal">undefined</span>;</span><br><span class="line">    <span class="variable language_">this</span>.<span class="property">currentMeshSet</span> = <span class="keyword">new</span> <span class="title class_">Set</span>();</span><br><span class="line"></span><br><span class="line">    <span class="variable language_">this</span>.<span class="property">scene</span>.<span class="property">background</span> = <span class="keyword">new</span> <span class="variable constant_">THREE</span>.<span class="title class_">Color</span>(<span class="string">&quot;white&quot;</span>);</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="comment">/**</span></span><br><span class="line"><span class="comment">   * 动画监听</span></span><br><span class="line"><span class="comment">   */</span></span><br><span class="line">  <span class="title function_">_animateListener</span>(<span class="params"></span>) &#123;</span><br><span class="line">    <span class="variable constant_">TWEEN</span>.<span class="title function_">update</span>();</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="comment">/**</span></span><br><span class="line"><span class="comment">   * 添加光照</span></span><br><span class="line"><span class="comment">   */</span></span><br><span class="line">  <span class="title function_">_addLight</span>(<span class="params">...pos</span>) &#123;</span><br><span class="line">    <span class="keyword">const</span> color = <span class="number">0xffffff</span>;</span><br><span class="line">    <span class="keyword">const</span> intensity = <span class="number">1</span>;</span><br><span class="line">    <span class="keyword">const</span> light = <span class="keyword">new</span> <span class="variable constant_">THREE</span>.<span class="title class_">DirectionalLight</span>(color, intensity);</span><br><span class="line">    light.<span class="property">position</span>.<span class="title function_">set</span>(-<span class="number">1</span>, <span class="number">2</span>, <span class="number">4</span>);</span><br><span class="line">    light.<span class="property">position</span>.<span class="title function_">set</span>(...pos);</span><br><span class="line">    <span class="variable language_">this</span>.<span class="property">scene</span>.<span class="title function_">add</span>(light);</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="comment">/**</span></span><br><span class="line"><span class="comment">   * 新建立方体</span></span><br><span class="line"><span class="comment">   * <span class="doctag">@param</span> &#123;<span class="type">*</span>&#125; <span class="variable">cubeCfg</span></span></span><br><span class="line"><span class="comment">   * <span class="doctag">@returns</span></span></span><br><span class="line"><span class="comment">   */</span></span><br><span class="line">  <span class="title function_">_createCubes</span>(<span class="params">cubeCfg</span>) &#123;</span><br><span class="line">    <span class="keyword">const</span> &#123; d, size, options &#125; = cubeCfg;</span><br><span class="line">    <span class="keyword">const</span> geometry = <span class="keyword">new</span> <span class="variable constant_">THREE</span>.<span class="title class_">BoxGeometry</span>(size, size, size);</span><br><span class="line">    <span class="keyword">const</span> cubeGroup = <span class="keyword">new</span> <span class="variable constant_">THREE</span>.<span class="title class_">Group</span>();</span><br><span class="line"></span><br><span class="line">    options.<span class="title function_">forEach</span>(<span class="function">(<span class="params">e</span>) =&gt;</span> &#123;</span><br><span class="line">      <span class="comment">// 立方体</span></span><br><span class="line">      <span class="keyword">const</span> material = <span class="keyword">new</span> <span class="variable constant_">THREE</span>.<span class="title class_">MeshPhongMaterial</span>(&#123;</span><br><span class="line">        <span class="attr">color</span>: <span class="title class_">ECubeType</span>[e.<span class="property">type</span>].<span class="property">color</span>,</span><br><span class="line">        <span class="attr">opacity</span>: <span class="number">0.5</span>,</span><br><span class="line">        <span class="attr">transparent</span>: <span class="literal">true</span>,</span><br><span class="line">        <span class="attr">side</span>: <span class="variable constant_">THREE</span>.<span class="property">DoubleSide</span>,</span><br><span class="line">      &#125;);</span><br><span class="line">      <span class="keyword">const</span> cube = <span class="keyword">new</span> <span class="variable constant_">THREE</span>.<span class="title class_">Mesh</span>(geometry, material);</span><br><span class="line">      cube.<span class="property">position</span>.<span class="title function_">set</span>(e.<span class="property">x</span> * d, e.<span class="property">y</span> * d, e.<span class="property">z</span> * d);</span><br><span class="line">      cube.<span class="property">name</span> = e.<span class="property">name</span>;</span><br><span class="line">      cube.<span class="property">rccs</span> = &#123;</span><br><span class="line">        <span class="attr">value</span>: <span class="title class_">ECubeType</span>[e.<span class="property">type</span>].<span class="property">value</span>,</span><br><span class="line">      &#125;;</span><br><span class="line">      <span class="comment">// 选中效果</span></span><br><span class="line">      <span class="keyword">const</span> box = <span class="keyword">new</span> <span class="variable constant_">THREE</span>.<span class="title class_">Box3</span>();</span><br><span class="line">      box.<span class="title function_">setFromCenterAndSize</span>(</span><br><span class="line">        <span class="keyword">new</span> <span class="variable constant_">THREE</span>.<span class="title class_">Vector3</span>(<span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>),</span><br><span class="line">        <span class="keyword">new</span> <span class="variable constant_">THREE</span>.<span class="title class_">Vector3</span>(size, size, size)</span><br><span class="line">      );</span><br><span class="line">      <span class="keyword">const</span> helper = <span class="keyword">new</span> <span class="variable constant_">THREE</span>.<span class="title class_">Box3Helper</span>(box, <span class="number">0xffff00</span>);</span><br><span class="line">      helper.<span class="property">visible</span> = <span class="literal">false</span>;</span><br><span class="line">      helper.<span class="property">name</span> = <span class="string">&quot;box3Helper&quot;</span>;</span><br><span class="line">      cube.<span class="title function_">add</span>(helper);</span><br><span class="line">      <span class="comment">// 触发选中效果</span></span><br><span class="line">      cube.<span class="property">trigger</span> = <span class="function">(<span class="params">visible</span>) =&gt;</span> &#123;</span><br><span class="line">        cube.<span class="property">children</span>[<span class="number">0</span>].<span class="property">visible</span> = visible;</span><br><span class="line">      &#125;;</span><br><span class="line">      cubeGroup.<span class="title function_">add</span>(cube);</span><br><span class="line">    &#125;);</span><br><span class="line">    <span class="keyword">return</span> cubeGroup;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="comment">/**</span></span><br><span class="line"><span class="comment">   * 创建射线组</span></span><br><span class="line"><span class="comment">   */</span></span><br><span class="line">  <span class="title function_">_createRaycasters</span>(<span class="params">d, size</span>) &#123;</span><br><span class="line">    <span class="keyword">const</span> raycasterOptions = [</span><br><span class="line">      <span class="comment">/**</span></span><br><span class="line"><span class="comment">       * 投射面: XY平面</span></span><br><span class="line"><span class="comment">       * 方向：Z负半轴</span></span><br><span class="line"><span class="comment">       */</span></span><br><span class="line">      &#123;</span><br><span class="line">        <span class="attr">name</span>: <span class="string">&quot;raycaster-1&quot;</span>,</span><br><span class="line">        <span class="attr">origin</span>: <span class="keyword">new</span> <span class="variable constant_">THREE</span>.<span class="title class_">Vector3</span>(d, d, d + size),</span><br><span class="line">        <span class="attr">direction</span>: <span class="keyword">new</span> <span class="variable constant_">THREE</span>.<span class="title class_">Vector3</span>(<span class="number">0</span>, <span class="number">0</span>, -<span class="number">1</span>),</span><br><span class="line">        <span class="attr">type</span>: <span class="string">&quot;solid&quot;</span>,</span><br><span class="line">      &#125;,</span><br><span class="line">      &#123;</span><br><span class="line">        <span class="attr">name</span>: <span class="string">&quot;raycaster-2&quot;</span>,</span><br><span class="line">        <span class="attr">origin</span>: <span class="keyword">new</span> <span class="variable constant_">THREE</span>.<span class="title class_">Vector3</span>(-d, d, d + size),</span><br><span class="line">        <span class="attr">direction</span>: <span class="keyword">new</span> <span class="variable constant_">THREE</span>.<span class="title class_">Vector3</span>(<span class="number">0</span>, <span class="number">0</span>, -<span class="number">1</span>),</span><br><span class="line">        <span class="attr">type</span>: <span class="string">&quot;solid&quot;</span>,</span><br><span class="line">      &#125;,</span><br><span class="line">      &#123;</span><br><span class="line">        <span class="attr">name</span>: <span class="string">&quot;raycaster-3&quot;</span>,</span><br><span class="line">        <span class="attr">origin</span>: <span class="keyword">new</span> <span class="variable constant_">THREE</span>.<span class="title class_">Vector3</span>(-d, -d, d + size),</span><br><span class="line">        <span class="attr">direction</span>: <span class="keyword">new</span> <span class="variable constant_">THREE</span>.<span class="title class_">Vector3</span>(<span class="number">0</span>, <span class="number">0</span>, -<span class="number">1</span>),</span><br><span class="line">        <span class="attr">type</span>: <span class="string">&quot;hyaline&quot;</span>,</span><br><span class="line">      &#125;,</span><br><span class="line">      &#123;</span><br><span class="line">        <span class="attr">name</span>: <span class="string">&quot;raycaster-4&quot;</span>,</span><br><span class="line">        <span class="attr">origin</span>: <span class="keyword">new</span> <span class="variable constant_">THREE</span>.<span class="title class_">Vector3</span>(d, -d, d + size),</span><br><span class="line">        <span class="attr">direction</span>: <span class="keyword">new</span> <span class="variable constant_">THREE</span>.<span class="title class_">Vector3</span>(<span class="number">0</span>, <span class="number">0</span>, -<span class="number">1</span>),</span><br><span class="line">        <span class="attr">type</span>: <span class="string">&quot;hyaline&quot;</span>,</span><br><span class="line">      &#125;,</span><br><span class="line">      <span class="comment">/**</span></span><br><span class="line"><span class="comment">       * 投射面: YZ平面</span></span><br><span class="line"><span class="comment">       * 方向：X负半轴</span></span><br><span class="line"><span class="comment">       */</span></span><br><span class="line">      &#123;</span><br><span class="line">        <span class="attr">name</span>: <span class="string">&quot;raycaster-5&quot;</span>,</span><br><span class="line">        <span class="attr">origin</span>: <span class="keyword">new</span> <span class="variable constant_">THREE</span>.<span class="title class_">Vector3</span>(d + size, d, d),</span><br><span class="line">        <span class="attr">direction</span>: <span class="keyword">new</span> <span class="variable constant_">THREE</span>.<span class="title class_">Vector3</span>(-<span class="number">1</span>, <span class="number">0</span>, <span class="number">0</span>),</span><br><span class="line">        <span class="attr">type</span>: <span class="string">&quot;solid&quot;</span>,</span><br><span class="line">      &#125;,</span><br><span class="line">      &#123;</span><br><span class="line">        <span class="attr">name</span>: <span class="string">&quot;raycaster-6&quot;</span>,</span><br><span class="line">        <span class="attr">origin</span>: <span class="keyword">new</span> <span class="variable constant_">THREE</span>.<span class="title class_">Vector3</span>(d + size, d, -d),</span><br><span class="line">        <span class="attr">direction</span>: <span class="keyword">new</span> <span class="variable constant_">THREE</span>.<span class="title class_">Vector3</span>(-<span class="number">1</span>, <span class="number">0</span>, <span class="number">0</span>),</span><br><span class="line">        <span class="attr">type</span>: <span class="string">&quot;solid&quot;</span>,</span><br><span class="line">      &#125;,</span><br><span class="line">      &#123;</span><br><span class="line">        <span class="attr">name</span>: <span class="string">&quot;raycaster-7&quot;</span>,</span><br><span class="line">        <span class="attr">origin</span>: <span class="keyword">new</span> <span class="variable constant_">THREE</span>.<span class="title class_">Vector3</span>(d + size, -d, -d),</span><br><span class="line">        <span class="attr">direction</span>: <span class="keyword">new</span> <span class="variable constant_">THREE</span>.<span class="title class_">Vector3</span>(-<span class="number">1</span>, <span class="number">0</span>, <span class="number">0</span>),</span><br><span class="line">        <span class="attr">type</span>: <span class="string">&quot;hyaline&quot;</span>,</span><br><span class="line">      &#125;,</span><br><span class="line">      &#123;</span><br><span class="line">        <span class="attr">name</span>: <span class="string">&quot;raycaster-8&quot;</span>,</span><br><span class="line">        <span class="attr">origin</span>: <span class="keyword">new</span> <span class="variable constant_">THREE</span>.<span class="title class_">Vector3</span>(d + size, -d, d),</span><br><span class="line">        <span class="attr">direction</span>: <span class="keyword">new</span> <span class="variable constant_">THREE</span>.<span class="title class_">Vector3</span>(-<span class="number">1</span>, <span class="number">0</span>, <span class="number">0</span>),</span><br><span class="line">        <span class="attr">type</span>: <span class="string">&quot;hyaline&quot;</span>,</span><br><span class="line">      &#125;,</span><br><span class="line">    ];</span><br><span class="line">    <span class="keyword">return</span> raycasterOptions;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="comment">/**</span></span><br><span class="line"><span class="comment">   * <span class="doctag">@descption</span> 增加投影面</span></span><br><span class="line"><span class="comment">   */</span></span><br><span class="line">  <span class="title function_">_addProjectionBoard</span>(<span class="params">raycasterOptions, d, size</span>) &#123;</span><br><span class="line">    <span class="keyword">const</span> boardGroup = <span class="keyword">new</span> <span class="variable constant_">THREE</span>.<span class="title class_">Group</span>();</span><br><span class="line"></span><br><span class="line">    raycasterOptions.<span class="title function_">forEach</span>(<span class="function">(<span class="params">e</span>) =&gt;</span> &#123;</span><br><span class="line">      <span class="keyword">const</span> dx = e.<span class="property">direction</span>.<span class="property">x</span> === -<span class="number">1</span> ? <span class="number">0.1</span> : size;</span><br><span class="line">      <span class="keyword">const</span> dy = size;</span><br><span class="line">      <span class="keyword">const</span> dz = e.<span class="property">direction</span>.<span class="property">z</span> === -<span class="number">1</span> ? <span class="number">0.1</span> : size;</span><br><span class="line">      <span class="keyword">const</span> geometry = <span class="keyword">new</span> <span class="variable constant_">THREE</span>.<span class="title class_">BoxGeometry</span>(</span><br><span class="line">        e.<span class="property">direction</span>.<span class="property">x</span> === -<span class="number">1</span> ? <span class="number">0.1</span> : size,</span><br><span class="line">        size,</span><br><span class="line">        e.<span class="property">direction</span>.<span class="property">z</span> === -<span class="number">1</span> ? <span class="number">0.1</span> : size</span><br><span class="line">      );</span><br><span class="line">      <span class="keyword">const</span> material = <span class="keyword">new</span> <span class="variable constant_">THREE</span>.<span class="title class_">MeshPhongMaterial</span>(&#123;</span><br><span class="line">        <span class="attr">color</span>: <span class="title class_">ECubeType</span>[e.<span class="property">type</span>].<span class="property">color</span>,</span><br><span class="line">        <span class="attr">opacity</span>: <span class="number">0.6</span>,</span><br><span class="line">        <span class="attr">transparent</span>: <span class="literal">true</span>,</span><br><span class="line">        <span class="attr">side</span>: <span class="variable constant_">THREE</span>.<span class="property">DoubleSide</span>,</span><br><span class="line">      &#125;);</span><br><span class="line">      <span class="keyword">const</span> board = <span class="keyword">new</span> <span class="variable constant_">THREE</span>.<span class="title class_">Mesh</span>(geometry, material);</span><br><span class="line">      board.<span class="property">position</span>.<span class="title function_">set</span>(</span><br><span class="line">        e.<span class="property">direction</span>.<span class="property">x</span> === -<span class="number">1</span> ? -e.<span class="property">origin</span>.<span class="property">x</span> : e.<span class="property">origin</span>.<span class="property">x</span>,</span><br><span class="line">        e.<span class="property">origin</span>.<span class="property">y</span>,</span><br><span class="line">        e.<span class="property">direction</span>.<span class="property">z</span> === -<span class="number">1</span> ? -e.<span class="property">origin</span>.<span class="property">z</span> : e.<span class="property">origin</span>.<span class="property">z</span></span><br><span class="line">      );</span><br><span class="line">      board.<span class="property">rccs</span> = &#123;</span><br><span class="line">        <span class="attr">value</span>: <span class="title class_">ECubeType</span>[e.<span class="property">type</span>].<span class="property">value</span>,</span><br><span class="line">      &#125;;</span><br><span class="line">      <span class="comment">// 检查效果</span></span><br><span class="line">      <span class="keyword">const</span> box = <span class="keyword">new</span> <span class="variable constant_">THREE</span>.<span class="title class_">Box3</span>();</span><br><span class="line">      box.<span class="title function_">setFromCenterAndSize</span>(</span><br><span class="line">        <span class="keyword">new</span> <span class="variable constant_">THREE</span>.<span class="title class_">Vector3</span>(<span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>),</span><br><span class="line">        <span class="keyword">new</span> <span class="variable constant_">THREE</span>.<span class="title class_">Vector3</span>(dx, dy, dz)</span><br><span class="line">      );</span><br><span class="line">      <span class="keyword">const</span> helper = <span class="keyword">new</span> <span class="variable constant_">THREE</span>.<span class="title class_">Box3Helper</span>(box, <span class="number">0xffff00</span>);</span><br><span class="line">      helper.<span class="property">visible</span> = <span class="literal">false</span>;</span><br><span class="line">      helper.<span class="property">name</span> = <span class="string">&quot;box3Helper&quot;</span>;</span><br><span class="line">      board.<span class="title function_">add</span>(helper);</span><br><span class="line">      <span class="comment">// 触发通过效果</span></span><br><span class="line">      board.<span class="property">trigger</span> = <span class="function">(<span class="params">visible</span>) =&gt;</span> &#123;</span><br><span class="line">        board.<span class="property">children</span>[<span class="number">0</span>].<span class="property">visible</span> = visible;</span><br><span class="line">      &#125;;</span><br><span class="line">      boardGroup.<span class="title function_">add</span>(board);</span><br><span class="line">    &#125;);</span><br><span class="line">    <span class="keyword">return</span> boardGroup;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="comment">/**</span></span><br><span class="line"><span class="comment">   * 按面选择</span></span><br><span class="line"><span class="comment">   * <span class="doctag">@param</span> &#123;<span class="type">*</span>&#125; <span class="variable">name</span></span></span><br><span class="line"><span class="comment">   */</span></span><br><span class="line">  <span class="title function_">selectFrame</span>(<span class="params">name</span>) &#123;</span><br><span class="line">    <span class="keyword">const</span> planeName = <span class="string">`plane-<span class="subst">$&#123;name&#125;</span>`</span>;</span><br><span class="line">    <span class="variable language_">this</span>.<span class="property">currentPlane</span> = <span class="variable language_">this</span>.<span class="property">planeOptions</span>.<span class="title function_">find</span>(<span class="function">(<span class="params">e</span>) =&gt;</span> e.<span class="property">name</span> === planeName);</span><br><span class="line">    <span class="variable language_">this</span>.<span class="property">currentMeshSet</span>.<span class="title function_">clear</span>();</span><br><span class="line">    <span class="variable language_">this</span>.<span class="property">cubeGroup</span>.<span class="property">children</span>.<span class="title function_">forEach</span>(<span class="function">(<span class="params">e</span>) =&gt;</span> &#123;</span><br><span class="line">      e.<span class="title function_">trigger</span>(<span class="literal">false</span>);</span><br><span class="line">    &#125;);</span><br><span class="line">    <span class="variable language_">this</span>.<span class="property">currentPlane</span>.<span class="property">raycasterIndexs</span>.<span class="title function_">forEach</span>(<span class="function">(<span class="params">e</span>) =&gt;</span> &#123;</span><br><span class="line">      <span class="keyword">const</span> &#123; origin, direction &#125; = <span class="variable language_">this</span>.<span class="property">raycasterOptions</span>[e];</span><br><span class="line">      <span class="variable language_">this</span>.<span class="property">gzsRaycaster</span>.<span class="title function_">set</span>(origin, direction);</span><br><span class="line">      <span class="keyword">const</span> intersects = <span class="variable language_">this</span>.<span class="property">gzsRaycaster</span>.<span class="title function_">intersectObjects</span>(</span><br><span class="line">        <span class="variable language_">this</span>.<span class="property">cubeGroup</span>.<span class="property">children</span></span><br><span class="line">      );</span><br><span class="line">      <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i &lt; intersects.<span class="property">length</span>; i++) &#123;</span><br><span class="line">        <span class="keyword">if</span> (intersects[i].<span class="property">object</span>.<span class="property">isMesh</span>) &#123;</span><br><span class="line">          <span class="variable language_">this</span>.<span class="property">currentMeshSet</span>.<span class="title function_">add</span>(intersects[i].<span class="property">object</span>);</span><br><span class="line">        &#125;</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;);</span><br><span class="line">    <span class="variable language_">this</span>.<span class="property">currentMeshSet</span>.<span class="title function_">forEach</span>(<span class="function">(<span class="params">e</span>) =&gt;</span> &#123;</span><br><span class="line">      e.<span class="title function_">trigger</span>(<span class="literal">true</span>);</span><br><span class="line">    &#125;);</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="title function_">rotateLeft</span>(<span class="params"></span>) &#123;</span><br><span class="line">    <span class="variable language_">this</span>.<span class="title function_">_rotate</span>(</span><br><span class="line">      <span class="variable language_">this</span>.<span class="property">currentMeshSet</span>,</span><br><span class="line">      <span class="variable language_">this</span>.<span class="property">currentPlane</span>.<span class="property">rotateAxis</span>,</span><br><span class="line">      <span class="variable language_">this</span>.<span class="property">currentPlane</span>.<span class="property">leftRad</span></span><br><span class="line">    );</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="title function_">rotateRight</span>(<span class="params"></span>) &#123;</span><br><span class="line">    <span class="variable language_">this</span>.<span class="title function_">_rotate</span>(</span><br><span class="line">      <span class="variable language_">this</span>.<span class="property">currentMeshSet</span>,</span><br><span class="line">      <span class="variable language_">this</span>.<span class="property">currentPlane</span>.<span class="property">rotateAxis</span>,</span><br><span class="line">      <span class="variable language_">this</span>.<span class="property">currentPlane</span>.<span class="property">rightRad</span></span><br><span class="line">    );</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="comment">/**</span></span><br><span class="line"><span class="comment">   * <span class="doctag">@descption</span> 旋转动画</span></span><br><span class="line"><span class="comment">   * <span class="doctag">@param</span> &#123;<span class="type">*</span>&#125; cubes 旋转的方块</span></span><br><span class="line"><span class="comment">   * <span class="doctag">@param</span> &#123;<span class="type">*</span>&#125; rotateAxis 旋转轴</span></span><br><span class="line"><span class="comment">   * <span class="doctag">@param</span> &#123;<span class="type">*</span>&#125; rad 旋转角度</span></span><br><span class="line"><span class="comment">   */</span></span><br><span class="line">  <span class="title function_">_rotate</span>(<span class="params">cubes, rotateAxis, rad</span>) &#123;</span><br><span class="line">    <span class="comment">// 更新起始数据</span></span><br><span class="line">    cubes.<span class="title function_">forEach</span>(<span class="function">(<span class="params">cube</span>) =&gt;</span> &#123;</span><br><span class="line">      cube.<span class="property">recoardStart</span> = &#123;</span><br><span class="line">        <span class="attr">position</span>: cube.<span class="property">position</span>.<span class="title function_">clone</span>(),</span><br><span class="line">        <span class="attr">rotation</span>: <span class="keyword">new</span> <span class="variable constant_">THREE</span>.<span class="title class_">Vector3</span>(<span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>),</span><br><span class="line">      &#125;;</span><br><span class="line">      cube.<span class="property">rotation</span>.<span class="property">x</span> = <span class="number">0</span>;</span><br><span class="line">      cube.<span class="property">rotation</span>.<span class="property">y</span> = <span class="number">0</span>;</span><br><span class="line">      cube.<span class="property">rotation</span>.<span class="property">z</span> = <span class="number">0</span>;</span><br><span class="line">    &#125;);</span><br><span class="line">    <span class="comment">// 旋转动画</span></span><br><span class="line">    <span class="keyword">const</span> <span class="variable constant_">LOCAL_AXIS</span> = rotateAxis.<span class="property">x</span> ? <span class="string">&quot;x&quot;</span> : rotateAxis.<span class="property">y</span> ? <span class="string">&quot;y&quot;</span> : <span class="string">&quot;z&quot;</span>;</span><br><span class="line">    <span class="keyword">const</span> <span class="variable constant_">TWEEN_DURATION</span> = <span class="number">1000</span>;</span><br><span class="line">    <span class="keyword">const</span> tween = <span class="keyword">new</span> <span class="variable constant_">TWEEN</span>.<span class="title class_">Tween</span>(&#123; <span class="attr">percentage</span>: <span class="number">0</span> &#125;);</span><br><span class="line">    tween.<span class="title function_">easing</span>(<span class="variable constant_">TWEEN</span>.<span class="property">Easing</span>.<span class="property">Quartic</span>.<span class="property">InOut</span>);</span><br><span class="line">    tween.<span class="title function_">onUpdate</span>(<span class="function">(<span class="params">&#123; percentage &#125;</span>) =&gt;</span> &#123;</span><br><span class="line">      cubes.<span class="title function_">forEach</span>(<span class="function">(<span class="params">cube</span>) =&gt;</span> &#123;</span><br><span class="line">        <span class="comment">// 旋转面</span></span><br><span class="line">        cube.<span class="property">rotation</span>[<span class="variable constant_">LOCAL_AXIS</span>] =</span><br><span class="line">          cube.<span class="property">recoardStart</span>.<span class="property">rotation</span>[<span class="variable constant_">LOCAL_AXIS</span>] + percentage * rad;</span><br><span class="line">        <span class="keyword">const</span> newPosition = cube.<span class="property">recoardStart</span>.<span class="property">position</span>.<span class="title function_">clone</span>();</span><br><span class="line">        newPosition.<span class="title function_">applyAxisAngle</span>(rotateAxis, percentage * rad);</span><br><span class="line">        <span class="comment">// 旋转位置</span></span><br><span class="line">        cube.<span class="property">position</span>.<span class="title function_">set</span>(newPosition.<span class="property">x</span>, newPosition.<span class="property">y</span>, newPosition.<span class="property">z</span>);</span><br><span class="line">      &#125;);</span><br><span class="line">    &#125;);</span><br><span class="line">    tween.<span class="title function_">onComplete</span>(<span class="function">() =&gt;</span> &#123;</span><br><span class="line">      <span class="comment">/** 校验结果 */</span></span><br><span class="line">      <span class="variable language_">this</span>.<span class="title function_">_check</span>();</span><br><span class="line">    &#125;);</span><br><span class="line">    tween.<span class="title function_">to</span>(&#123; <span class="attr">percentage</span>: <span class="number">1</span> &#125;, <span class="variable constant_">TWEEN_DURATION</span>);</span><br><span class="line">    tween.<span class="title function_">start</span>();</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="comment">/**</span></span><br><span class="line"><span class="comment">   * 校验结果</span></span><br><span class="line"><span class="comment">   */</span></span><br><span class="line">  <span class="title function_">_check</span>(<span class="params"></span>) &#123;</span><br><span class="line">    <span class="variable language_">this</span>.<span class="property">raycasterOptions</span>.<span class="title function_">forEach</span>(<span class="function">(<span class="params">e</span>) =&gt;</span> &#123;</span><br><span class="line">      <span class="keyword">const</span> checkSet = <span class="keyword">new</span> <span class="title class_">Set</span>();</span><br><span class="line">      <span class="variable language_">this</span>.<span class="property">gzsRaycaster</span>.<span class="title function_">set</span>(e.<span class="property">origin</span>, e.<span class="property">direction</span>);</span><br><span class="line">      <span class="keyword">const</span> intersects = <span class="variable language_">this</span>.<span class="property">gzsRaycaster</span>.<span class="title function_">intersectObjects</span>(</span><br><span class="line">        <span class="variable language_">this</span>.<span class="property">scene</span>.<span class="property">children</span></span><br><span class="line">      );</span><br><span class="line">      <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i &lt; intersects.<span class="property">length</span>; i++) &#123;</span><br><span class="line">        <span class="keyword">if</span> (intersects[i].<span class="property">object</span>.<span class="property">isMesh</span>) &#123;</span><br><span class="line">          checkSet.<span class="title function_">add</span>(intersects[i].<span class="property">object</span>);</span><br><span class="line">        &#125;</span><br><span class="line">      &#125;</span><br><span class="line">      <span class="keyword">const</span> checkArr = <span class="title class_">Array</span>.<span class="title function_">from</span>(checkSet);</span><br><span class="line">      checkArr[<span class="number">2</span>].<span class="title function_">trigger</span>(</span><br><span class="line">        checkArr[<span class="number">0</span>].<span class="property">rccs</span>.<span class="property">value</span> === checkArr[<span class="number">1</span>].<span class="property">rccs</span>.<span class="property">value</span> &amp;&amp;</span><br><span class="line">          checkArr[<span class="number">1</span>].<span class="property">rccs</span>.<span class="property">value</span> === checkArr[<span class="number">2</span>].<span class="property">rccs</span>.<span class="property">value</span></span><br><span class="line">      );</span><br><span class="line">    &#125;);</span><br><span class="line">    <span class="keyword">let</span> pass = <span class="variable language_">this</span>.<span class="property">boardGroup</span>.<span class="property">children</span>.<span class="title function_">every</span>(<span class="function">(<span class="params">e</span>) =&gt;</span> e.<span class="property">children</span>[<span class="number">0</span>].<span class="property">visible</span>);</span><br><span class="line">    <span class="built_in">setTimeout</span>(<span class="function">() =&gt;</span> &#123;</span><br><span class="line">      <span class="keyword">if</span> (pass) &#123;</span><br><span class="line">        <span class="title function_">alert</span>(<span class="string">&quot;出院！&quot;</span>);</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;, <span class="number">100</span>);</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure>]]></content>
    
    
    <summary type="html">&lt;p&gt;【枘凿六合】是《崩坏：星穹铁道》中的一款二阶魔方解密游戏，记录 Threejs 实现过程。&lt;/p&gt;</summary>
    
    
    
    <category term="Threejs" scheme="https://guangze233.com/categories/Threejs/"/>
    
    
    <category term="Three.js" scheme="https://guangze233.com/tags/Three-js/"/>
    
    <category term="WebGL" scheme="https://guangze233.com/tags/WebGL/"/>
    
    <category term="游戏开发" scheme="https://guangze233.com/tags/%E6%B8%B8%E6%88%8F%E5%BC%80%E5%8F%91/"/>
    
    <category term="魔方" scheme="https://guangze233.com/tags/%E9%AD%94%E6%96%B9/"/>
    
  </entry>
  
  <entry>
    <title>Hello World</title>
    <link href="https://guangze233.com/2023/08/24/hello-world/"/>
    <id>https://guangze233.com/2023/08/24/hello-world/</id>
    <published>2023-08-24T08:25:26.000Z</published>
    <updated>2026-08-01T15:43:06.586Z</updated>
    
    <content type="html"><![CDATA[<p>本站使用博客框架 <a href="https://hexo.io/">Hexo</a> 建站，配置参考 <a href="https://zhuanlan.zhihu.com/p/618864711">Hexo+Next主题搭建个人博客+优化全过程（完整详细版）</a>.</p><span id="more"></span><h2 id="建站准备"><a href="#建站准备" class="headerlink" title="建站准备"></a>建站准备</h2><ol><li>域名注册：<a href="https://buy.cloud.tencent.com/domain/">腾讯云·域名注册</a></li><li>购买服务器：<a href="https://buy.cloud.tencent.com/lighthouse">腾讯云·轻量应用服务器</a>, 时长至少3个月(备案用)</li><li>域名备案：<br>a. 腾讯云备案审核：<a href="https://console.cloud.tencent.com/beian/manage">腾讯云·网站备案</a>, 请查看不同省份的备案材料需求<br>b. 管局审核：<a href="https://beian.miit.gov.cn/">ICP/IP地址/域名信息备案管理系统</a>, 若未收到短信，可再次提交（腾讯云工作人员会主动联系）<br>c. 备案成功后处理说明：<a href="https://cloud.tencent.com/document/product/243/61412#.E5.A6.82.E4.BD.95.E6.B7.BB.E5.8A.A0.E7.BD.91.E7.AB.99.E5.A4.87.E6.A1.88.E5.8F.B7.EF.BC.9F">腾讯云·备案成功后处理说明</a></li><li>网站添加 https: <a href="https://cloud.tencent.com/document/product/400/35244?from_cn_redirect=1">Nginx 服务器 SSL 证书安装部署</a></li><li>工具：<a href="https://yasuo.xunjiepdf.com/img/">图片压缩软件在线版</a></li></ol><p>Welcome to <a href="https://hexo.io/">Hexo</a>! This is your very first post. Check <a href="https://hexo.io/docs/">documentation</a> for more info. If you get any problems when using Hexo, you can find the answer in <a href="https://hexo.io/docs/troubleshooting.html">troubleshooting</a> or you can ask me on <a href="https://github.com/hexojs/hexo/issues">GitHub</a>.</p><h2 id="Quick-Start"><a href="#Quick-Start" class="headerlink" title="Quick Start"></a>Quick Start</h2><h3 id="Create-a-new-post"><a href="#Create-a-new-post" class="headerlink" title="Create a new post"></a>Create a new post</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ hexo new <span class="string">&quot;My New Post&quot;</span></span><br></pre></td></tr></table></figure><p>More info: <a href="https://hexo.io/docs/writing.html">Writing</a></p><h3 id="Run-server"><a href="#Run-server" class="headerlink" title="Run server"></a>Run server</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ hexo server</span><br></pre></td></tr></table></figure><p>More info: <a href="https://hexo.io/docs/server.html">Server</a></p><h3 id="Generate-static-files"><a href="#Generate-static-files" class="headerlink" title="Generate static files"></a>Generate static files</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ hexo generate</span><br></pre></td></tr></table></figure><p>More info: <a href="https://hexo.io/docs/generating.html">Generating</a></p><h3 id="Deploy-to-remote-sites"><a href="#Deploy-to-remote-sites" class="headerlink" title="Deploy to remote sites"></a>Deploy to remote sites</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ hexo deploy</span><br></pre></td></tr></table></figure><p>More info: <a href="https://hexo.io/docs/one-command-deployment.html">Deployment</a></p>]]></content>
    
    
    <summary type="html">&lt;p&gt;本站使用博客框架 &lt;a href=&quot;https://hexo.io/&quot;&gt;Hexo&lt;/a&gt; 建站，配置参考 &lt;a href=&quot;https://zhuanlan.zhihu.com/p/618864711&quot;&gt;Hexo+Next主题搭建个人博客+优化全过程（完整详细版）&lt;/a&gt;.&lt;/p&gt;</summary>
    
    
    
    <category term="随笔" scheme="https://guangze233.com/categories/%E9%9A%8F%E7%AC%94/"/>
    
    
    <category term="Hexo" scheme="https://guangze233.com/tags/Hexo/"/>
    
    <category term="建站教程" scheme="https://guangze233.com/tags/%E5%BB%BA%E7%AB%99%E6%95%99%E7%A8%8B/"/>
    
  </entry>
  
</feed>
