1
0
mirror of https://github.com/ToxicCrack/PrintABrick.git synced 2025-05-16 20:30:09 -07:00

Add Part_Relation class

Add Part_Relation to store references between parts
This commit is contained in:
David Hübner 2017-03-14 18:46:20 +01:00
parent e8d3c937d8
commit 15089022f1
4 changed files with 393 additions and 106 deletions

View File

@ -26,11 +26,12 @@ class LoadLDRawLibraryCommand extends ContainerAwareCommand
//TODO log errors
try {
// TODO handle relative path to dir
if (($ldrawPath = $input->getArgument('ldraw_path')) == null) {
$ldrawPath = $ldrawLoader->downloadLibrary();
}
$ldrawLoader->loadModels($ldrawPath);
$ldrawLoader->loadData($ldrawPath);
} catch (\Exception $e) {
printf($e->getMessage());
}

View File

@ -4,12 +4,13 @@ namespace AppBundle\Entity\LDraw;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\Common\Collections\Criteria;
use Doctrine\ORM\Mapping as ORM;
/**
* Part.
*
* @ORM\Entity
* @ORM\Entity(repositoryClass="AppBundle\Repository\PartRepository")
* @ORM\Table(name="ldraw_part")
*/
class Part
@ -43,18 +44,18 @@ class Part
private $category;
/**
* @var Part
* @var Part_Relation
*
* @ORM\ManyToOne(targetEntity="AppBundle\Entity\LDraw\Part",cascade={"persist"})
* @ORM\OneToMany(targetEntity="AppBundle\Entity\LDraw\Part_Relation", mappedBy="parent")
*/
private $printOf;
private $relationsTo;
/**
* @var Part
* @var Part_Relation
*
* @ORM\ManyToOne(targetEntity="AppBundle\Entity\LDraw\Part",cascade={"persist"})
* @ORM\OneToMany(targetEntity="AppBundle\Entity\LDraw\Part_Relation", mappedBy="child")
*/
private $aliasOf;
private $relationsFrom;
/**
* @var Model
@ -73,6 +74,8 @@ class Part
public function __construct()
{
$this->keywords = new ArrayCollection();
$this->relationsTo = new ArrayCollection();
$this->relationsFrom = new ArrayCollection();
}
/**
@ -148,43 +151,35 @@ class Part
}
/**
* @return Part
* @return Part_Relation
*/
public function getPrintOf()
public function getRelationsTo()
{
return $this->printOf;
return $this->relationsTo;
}
/**
* @param Part $printOf
*
* @return Part
* @param Part_Relation $relationsTo
*/
public function setPrintOf($printOf)
public function setRelationsTo($relationsTo)
{
$this->printOf = $printOf;
return $this;
$this->relationsTo = $relationsTo;
}
/**
* @return Part
* @return Part_Relation
*/
public function getAliasOf()
public function getRelationsFrom()
{
return $this->aliasOf;
return $this->relationsFrom;
}
/**
* @param Part $printOf
*
* @return Part
* @param Part_Relation $relationsFrom
*/
public function setAliasOf($aliasOf)
public function setRelationsFrom($relationsFrom)
{
$this->aliasOf = $aliasOf;
return $this;
$this->relationsFrom = $relationsFrom;
}
/**
@ -193,11 +188,12 @@ class Part
public function getModel()
{
if (!$this->model) {
if ($this->printOf) {
return $this->printOf->getModel();
} elseif ($this->aliasOf) {
return $this->aliasOf->getModel();
if ($this->getPrintOf()) {
return $this->getPrintOf()->getModel();
} elseif ($this->getAliasOf()) {
return $this->getAliasOf()->getModel();
}
return null;
}
@ -246,10 +242,80 @@ class Part
*
* @return Part
*/
public function removePart(Keyword $keyword)
public function removeKeyword(Keyword $keyword)
{
$this->keywords->removeElement($keyword);
return $this;
}
private function getRelationOf($type)
{
$criteria = new Criteria();
$criteria->where(Criteria::expr()->eq('type', $type));
$relations = $this->relationsFrom->matching($criteria);
$array = new ArrayCollection();
foreach ($relations as $relation) {
$array->add($relation->getParent());
}
return $array;
}
private function getRelations($type)
{
$criteria = new Criteria();
$criteria->where(Criteria::expr()->eq('type', $type));
$relations = $this->relationsTo->matching($criteria);
$array = new ArrayCollection();
foreach ($relations as $relation) {
$array->add($relation->getChild());
}
return $array;
}
public function getPrintOf()
{
$parents = $this->getRelationOf('Print');
if (count($parents) > 0) {
return $parents->first();
}
return null;
}
public function getPrints()
{
return $this->getRelations('Print');
}
public function getSubpartOf()
{
return $this->getRelationOf('Subpart');
}
public function getSubparts()
{
return $this->getRelations('Subpart');
}
public function getAliasOf()
{
$parents = $this->getRelationOf('Alias');
if (count($parents) > 0) {
return $parents->first();
}
return null;
}
public function getAliases()
{
return $this->getRelations('Alias');
}
}

View File

@ -0,0 +1,109 @@
<?php
namespace AppBundle\Entity\LDraw;
use Doctrine\ORM\Mapping as ORM;
/**
* Part.
*
* @ORM\Entity
* @ORM\Table(name="ldraw_part_relation")
*/
class Part_Relation
{
/**
* @var Part
*
* @ORM\Id
* @ORM\ManyToOne(targetEntity="AppBundle\Entity\LDraw\Part", inversedBy="relationsTo", cascade={"persist"})
*/
private $parent;
/**
* @var Part
*
* @ORM\Id
* @ORM\ManyToOne(targetEntity="AppBundle\Entity\LDraw\Part", inversedBy="relationsFrom", cascade={"persist"} )
*/
private $child;
/**
* @var int
*
* @ORM\Column(type="integer")
*/
private $count;
/**
* @var string
*
* @ORM\Id
* @ORM\Column(type="string", length=255)
*/
private $type;
/**
* @return Part
*/
public function getParent()
{
return $this->parent;
}
/**
* @param Part $parent
*/
public function setParent($parent)
{
$this->parent = $parent;
}
/**
* @return Part
*/
public function getChild()
{
return $this->child;
}
/**
* @param Part $child
*/
public function setChild($child)
{
$this->child = $child;
}
/**
* @return int
*/
public function getCount()
{
return $this->count;
}
/**
* @param int $count
*/
public function setCount($count)
{
$this->count = $count;
}
/**
* @return string
*/
public function getType()
{
return $this->type;
}
/**
* @param string $type
*/
public function setType($type)
{
$this->type = $type;
}
}

View File

@ -6,14 +6,15 @@ use AppBundle\Entity\LDraw\Category;
use AppBundle\Entity\LDraw\Keyword;
use AppBundle\Entity\LDraw\Model;
use AppBundle\Entity\LDraw\Part;
use AppBundle\Entity\LDraw\Part_Relation;
use AppBundle\Entity\LDraw\Type;
use AppBundle\Service\LDViewService;
use League\Flysystem\Adapter\Local;
use League\Flysystem\Filesystem;
use Symfony\Component\Asset\Exception\LogicException;
use Symfony\Component\Console\Helper\ProgressBar;
use Symfony\Component\Finder\Finder;
//TODO refactor
class LDrawLoader extends Loader
{
/**
@ -21,6 +22,9 @@ class LDrawLoader extends Loader
*/
private $ldraw;
/**
* @var string download URL with current LDraw library
*/
private $ldraw_url;
/**
@ -37,6 +41,11 @@ class LDrawLoader extends Loader
$this->ldraw_url = $ldraw_url;
}
/**
* Download current LDraw library and extract it to system tmp directory.
*
* @return string Absolute path to temporary Ldraw library
*/
public function downloadLibrary()
{
$this->output->writeln('Downloading LDraw library form ldraw.org');
@ -57,19 +66,54 @@ class LDrawLoader extends Loader
return $temp_dir;
}
public function loadModels($LDrawLibrary)
/**
* @param $LDrawLibrary
*/
public function loadData($LDrawLibrary)
{
$adapter = new Local($LDrawLibrary);
$this->ldraw = new Filesystem($adapter);
// $files = $this->ldraw->get('parts')->getContents();
$this->loadParts();
}
/**
* Load stl model by calling LDViewSevice and create new Model.
*
* @param $file
* @param $header
*
* @throws \Exception
*
* @return Model|null
*/
private function loadModel($file, $header)
{
if (($model = $this->em->getRepository(Model::class)->find($header['id'])) == null) {
$model = new Model();
$model->setId($header['id']);
}
$model->setAuthor($header['author']);
$model->setModified($header['modified']);
try {
$stlFile = $this->LDViewService->datToStl($file, $this->ldraw)->getPath();
$model->setFile($stlFile);
} catch (\Exception $e) {
throw $e; //TODO
}
return $model;
}
// TODO refactor
public function loadParts()
{
$files = $this->ldraw->get('parts')->getContents();
$this->em->getConnection()->getConfiguration()->setSQLLogger(null);
// $finder = new Finder();
// $files = $finder->files()->name('*.dat')->depth('== 0')->in(getcwd().DIRECTORY_SEPARATOR.$LDrawLibrary.DIRECTORY_SEPARATOR.'parts');
$progressBar = new ProgressBar($this->output, count($files));
$progressBar->setFormat('very_verbose');
$progressBar->setMessage('Loading LDraw library models');
@ -110,40 +154,69 @@ class LDrawLoader extends Loader
}
}
if ($header['print_of_id']) {
if (($printParent = $this->em->getRepository(Part::class)->find($header['print_of_id'])) == null) {
if ($printParentId = $this->getPrinetedParentId($header['id'])) {
if (($printParent = $this->em->getRepository(Part::class)->find($printParentId)) == null) {
$printParent = new Part();
$printParent->setId($header['print_of_id']);
$printParent->setId($printParentId);
if (!$this->ldraw->has('parts/'.$printParentId.'.dat')) {
$printParent->setModel($this->loadModel($file, $header));
}
}
$part->setPrintOf($printParent);
if (($alias = $this->em->getRepository(Part_Relation::class)->find(['parent' => $printParent, 'child' => $part, 'type' => 'Print'])) == null) {
$alias = new Part_Relation();
$alias->setParent($printParent);
$alias->setChild($part);
$alias->setCount(0);
$alias->setType('Print');
}
$this->em->persist($alias);
}
if ($header['alias_of_id']) {
if (($aliasParent = $this->em->getRepository(Part::class)->find($header['alias_of_id'])) == null) {
$aliasParent = new Part();
$aliasParent->setId($header['alias_of_id']);
if (isset($header['subparts'])) {
$relationType = strpos($header['name'], '~Moved to ') === 0 ? 'Alias' : 'Subpart';
if ($header['type'] == 'Alias') {
if (count($header['subparts']) == 1) {
$relationType = 'Alias';
}
}
foreach ($header['subparts'] as $subId) {
if ($subId != $this->getPrinetedParentId($header['id'])) {
if ($this->ldraw->has('parts/'.$subId.'.dat') && $this->fileFilter($this->getPartHeader($this->ldraw->get('parts/'.$subId.'.dat')->getMetadata()))) {
if (($subPart = $this->em->getRepository(Part::class)->find($subId)) == null) {
$subPart = new Part();
$subPart->setId($subId);
$this->em->persist($subPart);
}
if (($alias = $this->em->getRepository(Part_Relation::class)->find(['parent' => $part, 'child' => $subPart, 'type' => $relationType])) == null) {
$alias = new Part_Relation();
$alias->setParent($part);
$alias->setChild($subPart);
$alias->setCount(0);
$alias->setType($relationType);
}
$alias->setCount($alias->getCount() + 1);
$this->em->persist($alias);
}
}
}
$part->setAliasOf($aliasParent);
}
if (!$header['print_of_id'] && !$header['alias_of_id']) {
if (($model = $this->em->getRepository(Model::class)->find($header['id'])) == null) {
$model = new Model();
$model->setId($header['id']);
}
if (!in_array($header['type'], ['Print', 'Alias'])) {
$part->setModel($this->loadModel($file, $header));
}
$model->setAuthor($header['author']);
$model->setModified($header['modified']);
try {
$file = $this->LDViewService->datToStl($file, $this->ldraw)->getPath();
} catch (\Exception $e) {
dump($e);
}
$model->setFile($file);
$part->setModel($model);
try {
$this->LDViewService->datToPng($file, $this->ldraw);
} catch (\Exception $e) {
dump($e->getMessage());
}
$this->em->persist($part);
@ -156,15 +229,26 @@ class LDrawLoader extends Loader
$progressBar->finish();
}
/**
* Determine if part file should be loaded into database.
*
* @param $header
*
* @return bool
*/
private function fileFilter($header)
{
if (strpos($header['name'], 'Sticker') !== 0 &&
(strpos($header['name'], '~') !== 0 || strpos($header['name'], '~Moved to ') === 0) &&
$header['type'] !== 'Subpart') {
// Do not include sticker parts and incomplete parts
if (strpos($header['name'], 'Sticker') !== 0 && strpos($header['id'], 's') !== 0 && $header['type'] != 'Subpart') {
// If file is alias of another part determine if referenced file should be included
if (strpos($header['name'], '~Moved to ') === 0) {
$filepath = str_replace('\\', DIRECTORY_SEPARATOR, 'parts/'.$header['alias_of_id'].'.dat');
// Get file path of referenced part file
$alias = 'parts/'.$this->getObsoleteParentId($header['name']).'.dat';
if ($this->ldraw->has($alias)) {
return $this->fileFilter($this->getPartHeader($this->ldraw->get($alias)->getMetadata()));
}
return $this->fileFilter($this->getPartHeader($this->ldraw->get($filepath)->getMetadata()));
return false;
}
return true;
@ -173,11 +257,19 @@ class LDrawLoader extends Loader
return false;
}
/**
* Get printed part parent id.
*
* part name in format:
* nnnPxx, nnnnPxx, nnnnnPxx, nnnaPxx, nnnnaPxx (a = alpha, n= numeric, x = alphanumeric)
*
* http://www.ldraw.org/library/tracker/ref/numberfaq/
*
*
* @param $filename
*/
private function getPrinetedParentId($filename)
{
// nnnPxx, nnnnPxx, nnnnnPxx, nnnaPxx, nnnnaPxx
// where (a = alpha, n= numeric, x = alphanumeric)
if (preg_match('/(^.*)(p[0-9a-z][0-9a-z][0-9a-z]{0,1})$/', $filename, $matches)) {
return $matches[1];
}
@ -185,16 +277,19 @@ class LDrawLoader extends Loader
return null;
}
private function isShortcutPart($filename)
{
// nnnCnn, nnnnCnn, nnnnnCnn Shortcut assembly of part nnn, nnnn or nnnnn with other parts or formed version of flexible part nnn, nnnn or nnnnn.
// nnnCnn-Fn, nnnnCnn-Fn, nnnnnCnn-Fn Positional variant of shortcut assembly of movable parts, comprising part nnn, nnnn or nnnnn with other parts.
// where (a = alpha, n= numeric, x = alphanumeric)
return preg_match('/(^.*)(c[0-9][0-9])(.*)/', $filename);
}
private function getAliasParentId($name)
/**
* Get parent of obsolete part kept for reference.
*
* part description in format:
* ~Moved to {new_number}
*
* http://www.ldraw.org/article/398.html (Appendix II (02-Oct-06))
*
* @param $name
*
* @return string|null Filename of referenced part
*/
private function getObsoleteParentId($name)
{
if (preg_match('/^(~Moved to )(.*)$/', $name, $matches)) {
return $matches[2];
@ -203,11 +298,21 @@ class LDrawLoader extends Loader
return null;
}
/**
* Get file reference from part line.
*
* Line type 1 is a sub-file reference. The generic format is:
* 1 <colour> x y z a b c d e f g h i <file>
*
* LDraw.org Standards: File Format 1.0.2 (http://www.ldraw.org/article/218.html)
*
* @param $line
*
* @return string|null Filename of referenced part
*/
private function getAlias($line)
{
// 1 <colour> x y z a b c d e f g h i <file>
if (preg_match('/^1(.*) (.*)\.dat$/', $line, $matches)) {
if (preg_match('/^1(.*) (.*)\.(dat|DAT)$/', $line, $matches)) {
return $matches[2];
}
@ -215,14 +320,27 @@ class LDrawLoader extends Loader
}
/**
* Parse LDraw .dat file header identifying model store data to array.
*
* [
* 'id' => string
* 'name' => string
* 'category' => string
* 'keywords' => []
* 'author' => string
* 'modified' => DateTime
* 'type' => string
* 'subparts' => []
* ]
*
* LDraw.org Standards: Official Library Header Specification (http://www.ldraw.org/article/398.html)
*
* @return array
*/
private function getPartHeader($file)
{
$header = [];
// $handle = fopen($file->getRealPath(), 'r');
$handle = $this->ldraw->readStream($file['path']);
if ($handle) {
@ -237,9 +355,9 @@ class LDrawLoader extends Loader
// 0 <CategoryName> <PartDescription>
if (!$firstLine) {
$array = explode(' ', trim($line), 2);
$header['category'] = isset($array[0]) ? ltrim($array[0], '=_~') : '';
$header['name'] = $line;
$array = explode(' ', ltrim(trim($line, 2), '=_~'));
$header['category'] = isset($array[0]) ? $array[0] : '';
$header['name'] = ltrim($line, '=_');
$firstLine = true;
}
@ -262,7 +380,8 @@ class LDrawLoader extends Loader
// 0 !LDRAW_ORG Part|Subpart|Primitive|48_Primitive|Shortcut (optional qualifier(s)) ORIGINAL|UPDATE YYYY-RR
elseif (strpos($line, '!LDRAW_ORG ') === 0) {
$type = preg_replace('/(^!LDRAW_ORG )(.*)( UPDATE| ORIGINAL)(.*)/', '$2', $line);
$header['type'] = $type;
$header['type'] = in_array($type, ['Part Alias', 'Shortcut Physical_Colour', 'Shortcut Alias', 'Part Physical_Colour']) ? 'Alias' : $type;
// Last modification date in format YYYY-RR
$date = preg_replace('/(^!LDRAW_ORG )(.*)( UPDATE | ORIGINAL )(.*)/', '$4', $line);
@ -273,25 +392,17 @@ class LDrawLoader extends Loader
}
}
} elseif (strpos($line, '1 ') === 0) {
if ($header['type'] == 'Part Alias' || $header['type'] == 'Shortcut Physical_Colour' || $header['type'] == 'Shortcut Alias') {
// "=" -> Alias name for other part kept for referece - do not include model -> LINK
// "_" -> Physical_color - do not include model -> LINK
$header['name'] = ltrim($header['name'], '=_');
$header['alias_of_id'] = $this->getAlias($line);
} elseif ($header['type'] == 'Shortcut') {
$header['subparts'][] = $this->getAlias($line);
}
$header['subparts'][] = $this->getAlias($line);
} elseif ($line != '') {
break;
}
}
if (isset($header['id'])) {
$header['print_of_id'] = $this->getPrinetedParentId($header['id']);
}
if (isset($header['name']) && !isset($header['alias_of_id'])) {
$header['alias_of_id'] = $this->getAliasParentId($header['name']);
if (strpos($header['name'], '~') === 0) {
$header['name'] = ltrim($header['name'], '~');
$header['type'] = 'Obsolete/Subpart';
} elseif ($printParentId = $this->getPrinetedParentId($header['id'])) {
$header['type'] = 'Print';
}
fclose($handle);